API Gateway Example in AWS CDK - Complete Guide

avatar
Borislav Hadzhiev

Last updated: Jan 26, 2024
5 min

banner

# Creating an API Gateway in AWS CDK

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-lib/aws-apigateway'; import * as cdk from 'aws-cdk-lib'; 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 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 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 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-lib/aws-apigateway'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as cdk from 'aws-cdk-lib'; 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_18_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 aws-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-lib/aws-apigateway'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as cdk from 'aws-cdk-lib'; 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_18_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 at /todos. The complete 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 aws-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, issue 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, run the destroy command:

shell
npx aws-cdk destroy

I've also written a tutorial on how to enable CORS for API Gateway or HTTP API in AWS CDK.

# Further Reading

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev