Chaining Requests
Let’s create your first code snippet! In the scanapi.yaml file, add the create_snippet request:
- name: create_snippet
  path: /snippets/
  method: post
  body:
    title: Hello World
    code: "print('hello world')"
    style: "xcode"
    language: "python"
  tests:
    - name: status_code_is_201
      assert: ${{ response.status_code == 201 }}
Putting it all together:
endpoints:
  - name: snippets-api
    path: http://demo.scanapi.dev/api/v1/
    headers:
      Content-Type: application/json
    requests:
      - name: health
        method: get
        path: /health/
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
          - name: body_equals_ok
            assert: ${{ response.json() == "OK!" }}
      - name: get_token
        path: /rest-auth/login/
        method: post
        body:
          username: ${USER}
          password: ${PASSWORD}
      # ALL BELOW IS NEW
      - name: create_snippet
        path: /snippets/
        method: post
        body:
          title: Hello World
          code: "print('hello world')"
          style: "xcode"
          language: "python"
        tests:
          - name: status_code_is_201
            assert: ${{ response.status_code == 201 }}
Run ScanAPI again and reload the report:
$ scanapi run
   
   
Oops, the response is Unauthorized. That makes the test fail and also, we could not create the
snippet code. To fix it, we need to send the Authentication Token received in the /login
response in the Authorization headers of /snippets.
In the scanapi.yaml file, in the get_token request, let’s store the received key in the token
variable:
vars:
  token: ${{response.json()["key"]}}
and in the create_snippet request, let’s send the token in via Authorization header:
headers:
  Authorization: Token ${token}
Putting it all together:
endpoints:
  - name: snippets-api
    path: http://demo.scanapi.dev/api/v1/
    headers:
      Content-Type: application/json
    requests:
      - name: health
        method: get
        path: /health/
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
          - name: body_equals_ok
            assert: ${{ response.json() == "OK!" }}
      - name: get_token
        path: /rest-auth/login/
        method: post
        body:
          username: ${USER}
          password: ${PASSWORD}
        vars: # this is new
          token: ${{response.json()["key"]}} # this is new
      - name: create_snippet
        path: /snippets/
        method: post
        headers: # this is new
          Authorization: Token ${token} # this is new
        body:
          title: Hello World
          code: "print('hello world')"
          style: "xcode"
          language: "python"
        tests:
          - name: status_code_is_201
            assert: ${{ response.status_code == 201 }}
Run ScanAPI again and reload the report
   
   
It works now! But, it is still missing one detail. Your key is being exposed in the request details
   
Let’s also hide this information. Add the Authorization headers to the hide_request in the
scanapi.conf:
report:
  hide_request:
    body:
      - password
    headers: # this is new
      - Authorization # this is new
  hide_response:
    body:
      - key
Run ScanAPI and reload the report again
   
All good, the sensitive information is hidden now. Using the same idea, lets chain dynamically one
more request. In the scanapi.yaml file, in the create_snippet request, let’s store the id of
the created snippet:
vars:
  snippet_id: ${{response.json()["id"]}}
and let’s create a new request to get the details of your brand new snippet:
- name: snippet_details
  path: /snippets/${snippet_id}/
  method: get
  tests:
    - name: status_code_is_200
      assert: ${{ response.status_code == 200 }}
Putting it all together:
endpoints:
  - name: snippets-api
    path: http://demo.scanapi.dev/api/v1/
    headers:
      Content-Type: application/json
    requests:
      - name: health
        method: get
        path: /health/
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
          - name: body_equals_ok
            assert: ${{ response.json() == "OK!" }}
      - name: get_token
        path: /rest-auth/login/
        method: post
        body:
          username: ${USER}
          password: ${PASSWORD}
        vars:
          token: ${{response.json()["key"]}}
      - name: create_snippet
        path: /snippets/
        method: post
        headers:
          Authorization: Token ${token}
        body:
          title: Hello World
          code: "print('hello world')"
          style: "xcode"
          language: "python"
        vars: # this is new
          snippet_id: ${{response.json()["id"]}} # this is new
        tests:
          - name: status_code_is_201
            assert: ${{ response.status_code == 201 }}
      - name: snippet_details # this is new
        path: /snippets/${snippet_id}/ # this is new
        method: get # this is new
        tests: # this is new
          - name: status_code_is_200 # this is new
            assert: ${{ response.status_code == 200 }} # this is new
Run ScanAPI again and reload the report
   
Let’s go ahead and add more snippet requests:
endpoints:
  - name: snippets-api
    path: http://demo.scanapi.dev/api/v1/
    headers:
      Content-Type: application/json
    requests:
      - name: health
        method: get
        path: /health/
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
          - name: body_equals_ok
            assert: ${{ response.json() == "OK!" }}
      - name: get_token
        path: /rest-auth/login/
        method: post
        body:
          username: ${USER}
          password: ${PASSWORD}
        vars:
          token: ${{response.json()["key"]}}
      - name: create_snippet
        path: /snippets/
        method: post
        headers:
          Authorization: Token ${token}
        body:
          title: Hello World
          code: "print('hello world')"
          style: "xcode"
          language: "python"
        vars:
          snippet_id: ${{response.json()["id"]}}
        tests:
          - name: status_code_is_201
            assert: ${{ response.status_code == 201 }}
      - name: snippet_details
        path: /snippets/${snippet_id}/
        method: get
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
      # ALL BELOW IS NEW
      - name: snippet_update_with_patch
        path: /snippets/${snippet_id}/
        method: patch
        headers:
          Authorization: Token ${token}
        body:
          code: "print('hello, patch')"
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
      - name: snippet_update_with_put
        path: /snippets/${snippet_id}/
        method: put
        headers:
          Authorization: Token ${token}
        body:
          title: Hello World - Ruby
          code: "puts 'hello world'"
          style: "emacs"
          language: "ruby"
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
      - name: delete_snippet
        path: snippets/${snippet_id}/
        method: delete
        headers:
          Authorization: Token ${token}
        tests:
          - name: status_code_is_204
            assert: ${{ response.status_code == 204 }}
      - name: snippets_list_all
        path: /snippets/
        method: get
        tests:
          - name: status_code_is_200
            assert: ${{ response.status_code == 200 }}
Run ScanAPI again and reload the report
   
Yay, you have finished testing and documenting the snippets requests using ScanAPI! 🎉
With the chaining requests feature, you can use any responses information from one request into the next requests via custom variables. This gives you huge flexibility and the power to test complex scenarios.
You might have noticed that the specification has a lot of repeated code. Let’s see how we can improve it using nested endpoints.