API Gateway Example in AWS CDK - Complete Guide

avatar

Borislav Hadzhiev

Sat May 01 20215 min read

banner

Photo by Ged Lawson

Creating an API Gateway in AWS CDK #

In this article we are going to cover a complete example of creating an API Gateway with Lambda integration.

In order to create an API Gateway in CDK, we have to instantiate the RestApi class.

The code for this article is available on GitHub

Let's start by creating the API Gateway.

lib/cdk-starter-stack.ts
import * as apigateway from '@aws-cdk/aws-apigateway'; import * as cdk from '@aws-cdk/core'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); const api = new apigateway.RestApi(this, 'api', { description: 'example api gateway', deployOptions: { stageName: 'dev', }, // ๐Ÿ‘‡ enable CORS defaultCorsPreflightOptions: { allowHeaders: [ 'Content-Type', 'X-Amz-Date', 'Authorization', 'X-Api-Key', ], allowMethods: ['OPTIONS', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE'], allowCredentials: true, allowOrigins: ['http://localhost:3000'], }, }); // ๐Ÿ‘‡ create an Output for the API URL new cdk.CfnOutput(this, 'apiUrl', {value: api.url}); } }

Let's go over the code snippet.

  1. we created an API Gateway by instantiating the RestApi class.
  2. we have passed the following props to the RestApi construct:
  • description - a short description of the API Gateway resource

  • deployOptions - options for the deployment stage of the API. We have updated the stage name of the API to dev. By default the stageName is set to prod. The name of the stage is used in the API url.

  • defaultCorsPreflightOptions - used to enable CORS at the API level. We won't need CORS, because we will test our API via the CLI.

    You would need to enable CORS in order to access an API Gateway from a client application, because the client application is hosted on a different domain than the API, which is hosted on https://amazonaws.com.

  1. we have added an Output with the API url, which we will write to a file when deploying the CDK stack
Writing the API url to a file is very convenient for keeping the value in sync between your frontend and backend code. On your frontend you can just import the value from the json output file.

Adding Resources to API Gateway in AWS CDK #

We are going to create 2 endpoints for our API:

  • /todos with HTTP GET
  • /todos/{todoId} with HTTP DELETE

We are going to integrate the API with lambda for both of the endpoints.

The code for this article is available on GitHub

Let's write the code for the GET /todos endpoint first:

lib/cdk-starter-stack.ts
import * as apigateway from '@aws-cdk/aws-apigateway'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as path from 'path'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props) // ... rest // ๐Ÿ‘‡ define GET todos function const getTodosLambda = new lambda.Function(this, 'get-todos-lambda', { runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.main', code: lambda.Code.fromAsset(path.join(__dirname, '/../src/get-todos')), }); // ๐Ÿ‘‡ add a /todos resource const todos = api.root.addResource('todos'); // ๐Ÿ‘‡ integrate GET /todos with getTodosLambda todos.addMethod( 'GET', new apigateway.LambdaIntegration(getTodosLambda, {proxy: true}), ); } }

Let's go over the code snippet.

  1. we created a lambda function by instantiating the Function class

  2. we added a /todos resource at the root of our API Gateway. Note that resources can be nested, i.e. we can have /todos/{todoId}, we are going to see an example of this later in the article

  3. we added the HTTP GET method to the /todos resource and integrated it with a lambda function.

    We set the proxy configuration option to true, which enables proxy integration for the method. Note that the default value for proxy is true, so we could have omitted the prop altogether. To use normal instead of proxy integration you would have to set proxy to false.

The code for this article is available on GitHub

Add the code for the lambda function at src/get-todos/index.js:

src/get-todos/index.js
async function main(event) { return { body: JSON.stringify([ {todoId: 1, text: 'walk the dog ๐Ÿ•'}, {todoId: 2, text: 'cook dinner ๐Ÿฅ—'}, ]), statusCode: 200, }; } module.exports = {main};

Let's deploy and test our API:

shell
npx cdk deploy \ --outputs-file ./cdk-outputs.json

We used the --outputs-file flag when deploying, in order to write the API url to a file named cdk-outputs.json in the root directory.

Notice how the API url includes the stage name, in our case dev. You would most likely set the stage name of the API, conditionally, depending on the environment.

If we look at the API Gateway management console, under Integration Request, we can see that the GET /todos resource is configured correctly:

api gateway get route

To test the integration with Lambda, we can query the API via the CLI. Replace YOUR_API_URL with the API url from the cdk-outputs.json file.

shell
curl --location \ --request \ GET 'YOUR_API_URL/todos'

The output from the command shows that we've configured our GET /todos resource successfully:

api get resource success

The code for this article is available on GitHub

Let's add a nested resource at DELETE /todos/{todoId} with Lambda integration.

lib/cdk-starter-stack.ts
import * as apigateway from '@aws-cdk/aws-apigateway'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as path from 'path'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props) // ... rest // ๐Ÿ‘‡ define delete todo function const deleteTodoLambda = new lambda.Function(this, 'delete-todo-lambda', { runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.main', code: lambda.Code.fromAsset(path.join(__dirname, '/../src/delete-todo')), }); // ๐Ÿ‘‡ add a /todos/{todoId} resource const todo = todos.addResource('{todoId}'); // ๐Ÿ‘‡ integrate DELETE /todos/{todoId} with deleteTodosLambda todo.addMethod( 'DELETE', new apigateway.LambdaIntegration(deleteTodoLambda), ); } }

Let's go over the code snippet.

  1. we created a lambda function
  2. we added an API Gateway resource at /todos/{todoId}. Note that this is a nested resource, because we've added the resource on the todos resource at /todos. The compete path of the resource becomes /todos/{todoId}.
  3. we added the HTTP DELETE method to the /todos/{todoId} resource and integrated it with lambda
We didn't have to pass the proxy property when integrating with lambda, because that's the default.

Create the file for the lambda function at src/delete-todo/index.js and add the following code to it:

src/delete-todo/index.js
async function main(event) { console.log('event is ๐Ÿ‘‰', JSON.stringify(event, null, 2)); return { body: JSON.stringify({ todoId: event.pathParameters.todoId, text: 'Buy groceries ๏ธ๐Ÿ•', }), statusCode: 200, }; } module.exports = {main};

In the lambda function we access the todoId path parameter from the proxy event and simply return it to the caller.

Let's deploy the second resource of our API Gateway:

shell
npx cdk deploy \ --outputs-file ./cdk-outputs.json

After a successful deployment, the resource at DELETE /todos/{todoId} has been added:

api gateway delete resource

In order to test the lambda integration at the DELETE /todos/{todoId} route, execute the following command:

shell
curl \ --location \ --request \ DELETE 'YOUR_API_URL/todos/my-todo-123'

You can grab the API url from the cdk-outputs.json file in the root directory.

The output from the command shows that the lambda integration for the nested resource at DELETE /todos/{todoId} has been configured successfully:

api gateway delete response

Clean up #

To delete the resources we have provisioned execute the destroy command:

shell
npx cdk destroy

Further Reading #

Add me on LinkedIn

I'm a Web Developer with TypeScript, React.js, Node.js and AWS experience.

Let's connect on LinkedIn

Join my newsletter

I'll send you 1 email a week with links to all of the articles I've written that week

Buy Me A Coffee