Last updated: Jan 26, 2024
Reading timeยท5 min
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.
Let's start by creating the API Gateway.
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.
RestApi
class.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
.
json
output file.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.
Let's write the code for the GET /todos
endpoint first.
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.
We created a lambda function by instantiating the Function class.
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.
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
.
Add the code for the lambda function at 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.
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.
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:
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.
curl --location \ --request \ GET 'YOUR_API_URL/todos'
The output from the command shows that we've configured our GET /todos
resource successfully:
Let's add a nested resource at DELETE /todos/{todoId}
with Lambda integration.
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.
/todos/{todoId}
. Note that this is a
nested resource at /todos
. The complete path of the resource becomes
/todos/{todoId}
.DELETE
method to the /todos/{todoId}
resource and
integrated it with Lambda.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:
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.
npx aws-cdk deploy \ --outputs-file ./cdk-outputs.json
After a successful deployment, the resource at DELETE /todos/{todoId}
has been
added:
In order to test the lambda integration at the DELETE /todos/{todoId}
route,
issue the following command:
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.
To delete the resources we have provisioned, run the destroy
command:
npx aws-cdk destroy
I've also written a tutorial on how to enable CORS for API Gateway or HTTP API in AWS CDK.