Http API Example in AWS CDK [API Gateway V2]

avatar

Borislav Hadzhiev

Wed May 12 20214 min read

banner

Photo by David Tapia

Creating an Http API in AWS CDK #

In this article we're going to create an Http API with 2 routes:

  • GET /todos - with a Lambda function integration
  • DELETE /todos/{todoId} - with a Lambda function integration

Http APIs have 2 main components:

  • routes - the Http method and path of the request, e.g. GET /articles
  • integration - how the Http API should respond to requests to a specific route, e.g. a lambda function gets invoked and returns a response

In order to create an Http API in CDK, we have to instantiate and configure the HttpApi class.

The code for this article is available on GitHub

Let's start by creating an Http API, a lambda function and an API route at GET /todos:

lib/cdk-starter-stack.ts
import {CorsHttpMethod, HttpApi, HttpMethod} from '@aws-cdk/aws-apigatewayv2'; import {LambdaProxyIntegration} from '@aws-cdk/aws-apigatewayv2-integrations'; 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); // ๐Ÿ‘‡ create our HTTP Api const httpApi = new HttpApi(this, 'http-api-example', { description: 'HTTP API example', corsPreflight: { allowHeaders: [ 'Content-Type', 'X-Amz-Date', 'Authorization', 'X-Api-Key', ], allowMethods: [ CorsHttpMethod.OPTIONS, CorsHttpMethod.GET, CorsHttpMethod.POST, CorsHttpMethod.PUT, CorsHttpMethod.PATCH, CorsHttpMethod.DELETE, ], allowCredentials: true, allowOrigins: ['http://localhost:3000'], }, }); // ๐Ÿ‘‡ create get-todos Lambda const getTodosLambda = new lambda.Function(this, 'get-todos', { runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.main', code: lambda.Code.fromAsset(path.join(__dirname, '/../src/get-todos')), }); // ๐Ÿ‘‡ add route for GET /todos httpApi.addRoutes({ path: '/todos', methods: [HttpMethod.GET], integration: new LambdaProxyIntegration({ handler: getTodosLambda, }), }); } }

Let's go over what we did in the code snippet.

  1. we created an Http API, by instantiating the HttpApi class. We've enabled CORS on the API, even though we won't need it, because we'll be testing the API through the CLI. You can read more about CORS with Http APIs in my other article - Enable CORS for HTTP API in AWS CDK

  2. we created a lambda function, which we will integrate with our Http API at GET /todos

  3. we added a route to our API, specifying that all GET requests made to the /todos path, should get routed to our lambda function

The code for this article is available on GitHub

Let's add the code for our lambda function at src/get-todos/index.js:

src/get-todos/index.js
async function main(event) { return { body: JSON.stringify([ {taskId: 1, text: 'buy groceries ๐Ÿ›๏ธ'}, {taskId: 2, text: 'wash the dishes ๐Ÿฝ๏ธ'}, ]), statusCode: 200, }; } module.exports = {main};

The lambda function simply returns a JSON responses of an array with todo items.

The code for this article is available on GitHub

Before we deploy and test our API, let's also add a route with a path parameter at DELETE /todos/{todoId}.

lib/cdk-starter-stack.ts
import {CorsHttpMethod, HttpApi, HttpMethod} from '@aws-cdk/aws-apigatewayv2'; import {LambdaProxyIntegration} from '@aws-cdk/aws-apigatewayv2-integrations'; 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 // ๐Ÿ‘‡ create delete-todos Lambda const deleteTodoLambda = new lambda.Function(this, 'delete-todo', { runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.main', code: lambda.Code.fromAsset(path.join(__dirname, '/../src/delete-todo')), }); // ๐Ÿ‘‡ add route for DELETE /todos/{todoId} httpApi.addRoutes({ path: '/todos/{todoId}', methods: [HttpMethod.DELETE], integration: new LambdaProxyIntegration({ handler: deleteTodoLambda, }), }); // ๐Ÿ‘‡ add an Output with the API Url new cdk.CfnOutput(this, 'apiUrl', { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion value: httpApi.url!, }); } }

Let's go over what we did in the code snippet.

  1. we created a lambda function, which we'll integrate for Http DELETE at /todos/{todoId}

  2. we added another route to our Http API. The route is for DELETE requests to /todos/{todoId}, i.e. /todos/todo-123. The requests to this route will get forwarded to our delete todo lambda function

  3. we added an Output with the Http API url, which we'll write to a file on the local file system

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.

Let's add the code for the delete-todo lambda function at src/delete-todo/index.js:

The code for this article is available on GitHub
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};

The function does pretty much the same as our get-todos function - returns a JSON response.

Let's execute a deployment to test our Http API. We'll write the API url to a file on the local file system:

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

After a successful deployment we can look at the integrations for our Http API in the API Gateway management console:

http api integrations

Everything seems to be configured as expected, we have:

  • GET /todos route with Lambda function integration
  • DELETE /todos/{todoId} route with Lambda function integration

Let's test our GET /todos route first. You can grab the API url from the management console or from the output in the cdk-outputs.json file.

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

The response shows that our lambda integration for the GET /todos route works as expected:

http api get route

Finally, let's test our DELETE /todos/{todoId} route:

shell
curl --location --request DELETE 'YOUR_API_URL/todos/todo-1234'

Our lambda integration for the DELETE /todos/{todoId} route also works:

http api delete route

At this point we have a working HTTP Api with lambda integrations for 2 routes.

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