4. Amazon API Gateway
Amazon API Gateway is a very flexible cloud service, mostly used to create a REST API powered by a lambda function. I am going to take you through the instructions on how to create a simple HTTP GET endpoint with the help of the Serverless Framework, and how we can return a response in JSON.
Although the following instruction are simple, API Gateway is a very powerful tool that can handle things such as:
- Serverless Websockets
- The Validation of input data in accordance with its own JSON schema
- The generation of Swagger documentation
- Authorization with an API key
- HTTP request and response monitoring
- Authorization controlled by its own lambda function
- The settings of different kinds of limits and throttling requirements for various API keys
- AWS REST API proxy (advanced design pattern)
and many others…
Complete source code illustration sample:
Serverless.yml
In a serverless.yml
file we define a “customers” function, which processes API requests. It has to be set up in events
, the so-called “http” event source, which ensures that the function is executed when receiving a GET request from the address /customers
.
service: four-api-gateway
plugins:
- serverless-offline
provider:
name: aws
runtime: nodejs12.x
region: eu-central-1
functions:
customers:
handler: src/customers.handler
events:
- http:
path: /customers
method: get
The Handler
Every lambda function must have a “handler” function, which is called each time the handler function is invoked. In the case of JavaScript; the handler can take two forms – a promise (async/await) – the same as in our example, or the classic callback (for further information click here).
'use strict'
module.exports.handler = async (event) => {
console.log('Načítám klienty z databáze...')
console.log(JSON.stringify(event, null, 2))
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
customers: [
{
id: 1,
name: 'Karel'
},
{
id: 2,
name: 'Pepa'
},
{
id: 3,
name: 'Janek'
}
]
})
}
}
The response structure
statusCode
– the HTTP status code of a responseheaders
– HTTP header we want to send in the responsebody
– the body of the HTTP response, which must always be a string, therefore we useJSON.stringify
Here is an example of the contents of the API Gateway input event
{
"resource": "/customers",
"path": "/customers",
"httpMethod": "GET",
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "CZ",
"Cookie": "ajs_user_id=null",
"Host": "96g84zqz75.execute-api.eu-central-1.amazonaws.com",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"User-Agent": "......",
"Via": "2.0 7da8d24daaa6257fb28a90cd4a3bbe5d.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "cALrq-Aso-SFccHhkGlqespelPm3PnDWOFLo7NhnJk5Cx-m0jliUdg==",
"X-Amzn-Trace-Id": "Root=1-5f08d25f-3851b6fdbb7c12f7da82c599",
"X-Forwarded-For": "88.112.141.53, 130.176.34.83",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"multiValueHeaders": {
"Accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
],
"Accept-Encoding": ["gzip, deflate, br"],
"Accept-Language": ["en-GB,en-US;q=0.9,en;q=0.8"],
"CloudFront-Forwarded-Proto": ["https"],
"CloudFront-Is-Desktop-Viewer": ["true"],
"CloudFront-Is-Mobile-Viewer": ["false"],
"CloudFront-Is-SmartTV-Viewer": ["false"],
"CloudFront-Is-Tablet-Viewer": ["false"],
"CloudFront-Viewer-Country": ["CZ"],
"Cookie": ["ajs_user_id=null"],
"Host": ["96g84zqz75.execute-api.eu-central-1.amazonaws.com"],
"sec-fetch-dest": ["document"],
"sec-fetch-mode": ["navigate"],
"sec-fetch-site": ["none"],
"sec-fetch-user": ["?1"],
"upgrade-insecure-requests": ["1"],
"User-Agent": [
"......"
],
"Via": ["2.0 7da8d24baay6657fb28a90cd4a3bbe5d.cloudfront.net (CloudFront)"],
"X-Amz-Cf-Id": ["cALrq-Aso-SFtcHhkGlqespelPm5PnDWOFLo7NhnKk5Cx-m0jliUdg=="],
"X-Amzn-Trace-Id": ["Root=1-5f08d25f-1851b6fdbb7c12f7da02w199"],
"X-Forwarded-For": ["85.212.151.53, 120.876.44.13"],
"X-Forwarded-Port": ["443"],
"X-Forwarded-Proto": ["https"]
},
"queryStringParameters": null,
"multiValueQueryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"resourceId": "n0mk11",
"resourcePath": "/customers",
"httpMethod": "GET",
"extendedRequestId": "PeXO5H6DFiAFgxw=",
"requestTime": "10/Jul/2020:20:41:03 +0000",
"path": "/dev/customers",
"accountId": ".......",
"protocol": "HTTP/1.1",
"stage": "dev",
"domainPrefix": "96g8b1h47j",
"requestTimeEpoch": 1594413663007,
"requestId": "77691ybt-n351-4cde-9db7-f8b5f64e0137",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"sourceIp": "81.412.743.14",
"principalOrgId": null,
"accessKey": null,
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "......",
"user": null
},
"domainName": "96g8b1h47j.execute-api.eu-central-1.amazonaws.com",
"apiId": "96g8b1h47j"
},
"body": null,
"isBase64Encoded": false
}
Local development
serverless-offline
$ serverless offline
is a popular plugin for the Serverless Framework, which enables local development of the REST API.
Installation: $ npm install serverless-offline --save-dev
In the sample repository, the local server is started by:$ npm run dev
$ npm run dev
> 4-api-gateway@1.0.0 dev ......./4-api-gateway
> serverless offline
Serverless: Starting Offline: dev/eu-central-1.
Serverless: Routes for customers:
Serverless: GET /customers
Serverless: POST /{apiVersion}/functions/four-api-gateway-dev-customers/invocations
Serverless: Offline [HTTP] listening on http://localhost:3000
Serverless: Enter "rp" to replay the last request
Once running, you should see that the local API test request now works 🎉
$ curl http://localhost:3000/customers
{"customers":[{"id":1,"name":"Karel"},{"id":2,"name":"Pepa"},{"id":3,"name":"Janek"}]}
Deployment on the AWS
$ serverless deploy
In the sample repository, deployment to AWS is done by the command:$ npm run deploy
$ npm run deploy
> 4-api-gateway@1.0.0 deploy ......./4-api-gateway
> serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service four-api-gateway.zip file to S3 (91.23 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...................................
Serverless: Stack update finished...
Service Information
service: four-api-gateway
stage: dev
region: eu-central-1
stack: four-api-gateway-dev
resources: 11
api keys:
None
endpoints:
GET - https://n7rex1gz2i.execute-api.eu-central-1.amazonaws.com/dev/customers
functions:
customers: four-api-gateway-dev-customers
layers:
None
Again, we should see that the test request for the deployed API works 🎉
$ curl https://n7rex1gz2i.execute-api.eu-central-1.amazonaws.com/dev/customers
{"customers":[{"id":1,"name":"Karel"},{"id":2,"name":"Pepa"},{"id":3,"name":"Janek"}]}
Logs
The logs from the “customers” Lambda are available in Amazon CloudWatch, in the log group called: /aws/lambda/four-api-gateway-dev-customers
Viewing API details in the AWS console
The newly created API can also be seen in the AWS console. It is not recommended that any manual changes be made here, as to prevent any collisions with the Serverless Framework.
Information about the deployed application
$ serverless info -v
If you want to go back, for example, to the URL of the “customers” endpoint, the only thing you need to do, in the case of the sample repository, is to run:$ npm run info
$ npm run info
> 4-api-gateway@1.0.0 info ......./4-api-gateway
> serverless info -v
Service Information
service: four-api-gateway
stage: dev
region: eu-central-1
stack: four-api-gateway-dev
resources: 11
api keys:
None
endpoints:
GET - https://n7rex1gz2i.execute-api.eu-central-1.amazonaws.com/dev/customers
functions:
customers: four-api-gateway-dev-customers
layers:
None
Stack Outputs
CustomersLambdaFunctionQualifiedArn: arn:aws:lambda:eu-central-1:124515691641:function:four-api-gateway-dev-customers:3
ServiceEndpoint: https://n7rex1gz2i.execute-api.eu-central-1.amazonaws.com/dev
ServerlessDeploymentBucketName: four-api-gateway-dev-serverlessdeploymentbucket-1ujue1xbdlkvw
Removal from the AWS
$ serverless remove
The sample repository is removed from the AWS by using the command:$ npm run remove
$ npm run remove
> 4-api-gateway@1.0.0 remove ......./4-api-gateway
> serverless remove
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
...........
Serverless: Stack removal finished...
As expected, testing the request for the removed API does not work anymore 🚫 👍
$ curl https://n7rex1gz2i.execute-api.eu-central-1.amazonaws.com/dev/customers
curl: (6) Could not resolve host: n7rex1gz2i.execute-api.eu-central-1.amazonaws.com
Conclusion
As I mentioned in the introduction, API Gateway is a very powerful tool and you are surely going to see more articles about it on our blog.
In case you have any questions, feel free to contact me at Twitter @FilipPyrek.
With ❤️ made in Brno.