Now that we have our backend completely set up and secured, let’s test the API we just deployed.

To be able to hit our API endpoints securely, we need to follow these steps.

  1. Authenticate against our User Pool and acquire a user token.
  2. With the user token get temporary IAM credentials from our Identity Pool.
  3. Use the IAM credentials to sign our API request with Signature Version 4.

These steps can be a bit tricky to do by hand. So we created a simple tool called AWS API Gateway Test CLI.

You can install it by running the following.

$ npm install -g aws-api-gateway-cli-test

We need to pass in quite a bit of our info to complete the above steps.

  • Use the username and password of the user created in the Create a Cognito test user chapter.
  • Replace YOUR_COGNITO_USER_POOL_ID, YOUR_COGNITO_APP_CLIENT_ID, and YOUR_COGNITO_REGION with the values from the Create a Cognito user pool chapter. In our case the region is us-east-1.
  • Replace YOUR_IDENTITY_POOL_ID with the one from the Create a Cognito identity pool chapter.
  • Use the YOUR_API_GATEWAY_URL and YOUR_API_GATEWAY_REGION with the ones from the Deploy the APIs chapter. In our case the URL is and the region is us-east-1.

And run the following.

$ apig-test \
--username='' \
--password='Passw0rd!' \
--user-pool-id='YOUR_COGNITO_USER_POOL_ID' \
--app-client-id='YOUR_COGNITO_APP_CLIENT_ID' \
--cognito-region='YOUR_COGNITO_REGION' \
--identity-pool-id='YOUR_IDENTITY_POOL_ID' \
--invoke-url='YOUR_API_GATEWAY_URL' \
--api-gateway-region='YOUR_API_GATEWAY_REGION' \
--path-template='/notes' \
--method='POST' \
--body='{"content":"hello world","attachment":"hello.jpg"}'

While this might look intimidating, just keep in mind that behind the scenes all we are doing is generating some security headers before making a basic HTTP request. You’ll see more of this process when we connect our React.js app to our API backend.

If you are on Windows, use the command below. The space between each option is very important.

$ apig-test --username --password Passw0rd! --user-pool-id YOUR_COGNITO_USER_POOL_ID --app-client-id YOUR_COGNITO_APP_CLIENT_ID --cognito-region YOUR_COGNITO_REGION --identity-pool-id YOUR_IDENTITY_POOL_ID --invoke-url YOUR_API_GATEWAY_URL --api-gateway-region YOUR_API_GATEWAY_REGION --path-template /notes --method POST --body "{\"content\":\"hello world\",\"attachment\":\"hello.jpg\"}"

If the command is successful, the response will look similar to this.

Authenticating with User Pool
Getting temporary credentials
Making API request
{ status: 200,
  statusText: 'OK',
   { userId: 'us-east-1:9bdc031d-ee9e-4ffa-9a2d-123456789',
     noteId: '8f7da030-650b-11e7-a661-123456789',
     content: 'hello world',
     attachment: 'hello.jpg',
     createdAt: 1499648598452 } }

And that’s it for the backend! Next we are going to move on to creating the frontend of our app.

Common Issues

  • Response {status: 403}

    This is the most common issue we come across and it is a bit cryptic and can be hard to debug. Here are a few things to check before you start debugging:

    • Ensure the --path-template option in the apig-test command is pointing to /notes and not notes. The format matters for securely signing our request.

    • There are no trailing slashes for YOUR_API_GATEWAY_URL. In our case, the URL is Notice that it does not end with a /.

    There is a good chance that this error is happening even before our Lambda functions are invoked. So we can start by making sure our IAM Roles are configured properly for our Identity Pool. Follow the steps as detailed in our Debugging Serverless API Issues chapter to ensure that your IAM Roles have the right set of permissions.

    Next, you can enable API Gateway logs and follow these instructions to read the requests that are being logged. This should give you a better idea of what is going on.

    Finally, make sure to look at the comment thread below. We’ve helped quite a few people with similar issues and it’s very likely that somebody has run into a similar issue as you.

  • Response {status: false}

    If instead your command fails with the {status: false} response; we can do a few things to debug this. This response is generated by our Lambda functions when there is an error. Add a console.log like so in your handler function.

    catch(e) {
      callback(null, failure({status: false}));

    And deploy it using serverless deploy function -f create. But we can’t see this output when we make an HTTP request to it, since the console logs are not sent in our HTTP responses. We need to check the logs to see this. We have a detailed chapter on working with API Gateway and Lambda logs and you can read about how to check your debug messages here.

    A common source of errors here is an improperly indented serverless.yml. Make sure to double-check the indenting in your serverless.yml by comparing it to the one from this chapter.