How to Build an API Gateway REST API with Lambda Integration
In this tutorial, we’ll walk through setting up a REST API in AWS API Gateway with Lambda Integration, configuring various integration methods and handling method requests and responses.
Introduction
API Gateway Lambda integration allows us to connect our API Gateway resources to Lambda functions and use them as backend logic for our API. There are two types of Lambda integrations:
Lambda Proxy Integration
This approach simplifies development by offloading request parsing and response formatting to API Gateway. This object is then passed to the Lambda function as an event object. The Lambda function processes the event object and returns a JSON response object, which API Gateway further parses and sends back to the client as the API response. This is suitable for most API implementations with JSON input and output formats.
Lambda Non-Proxy Integration
This approach provides greater flexibility and control over the request and response format. The Lambda function parses the request, processes it, and generates the response. This approach provides greater flexibility and control over the request and response format but requires more development effort as the Lambda function needs to handle parsing and formatting.
Implementation
Let’s extend the tutorial by creating a simple CRUD API for managing a list of books.
Step 1: Create an API in API Gateway
In AWS API Gateway, when we create a new API, we have the option to choose between REST API and HTTP API. REST APIs offer more advanced features and capabilities, making them suitable for complex API scenarios. HTTP APIs prioritize simplicity and clarity, making them a perfect fit for streamlined use cases.
Create an API
In the API Gateway console, choose Create API
and select REST API
. Give it a descriptive name like Book Management API
. Select Regional
as the desired API endpoint type then click on Create API
.
Step 2: Deploy a Lambda function
Next, we need to create Lambda functions to handle the logic for each method. Having a separate Lambda function for each resource method (e.g., Create, Read, Update, Delete) is a common and recommended best practice. This approach follows the Single Responsibility Principle and helps maintain a clean and modular codebase.
Create Function (Create a new book):
import json
def create_book(event, context):
request_body = json.loads(event['body'])
# Validate required fields (e.g., title, author, etc.)
if not request_body.get('title') or not request_body.get('author'):
return {
'statusCode': 400,
'body': json.dumps({'message': 'Title and Author are required.'}),
}
new_book = {
'id': 123, # Replace with a proper ID generation method
'title': request_body['title'],
'author': request_body['author'],
# Other book details...
}
return {
'statusCode': 201,
'body': json.dumps(new_book),
}
Read Function (Create a book by ID):
import json
def get_book(event, context):
book_id = event['bookId']
book = {
"bookId": 123,
"bookTitle": "Harry Potter",
"authorName": "J. K. Rowling"
}
if not book_id == "123":
return {
'statusCode': 404,
'body': json.dumps({'message': 'Book not found.'}),
}
return {
'statusCode': 200,
'body': json.dumps(book),
}
Step 3: Create Resources and Methods
Next, create resources for the API endpoints, e.g., /books
for listing and creating books, and /books/{id}
for getting, updating, and deleting individual books. Define appropriate HTTP methods (GET, POST, PUT, DELETE) for each resource.
Step 4: Test the API
API Gateway provides a testing feature called Test
for API methods, allowing us to test them before deploying to a stage.
In the API Gateway console, navigate to our API and select the resource/method we want to test. Click on the Test
tab. On the test page, we can configure a test event. This is a JSON payload that simulates the input to your API method.
Let’s test the POST
request by inputting the request body as below:
Click the Test
button to simulate the API request using the configured test event. Review the results, including the request sent and the response received.
The log displays empty values for the method request path, query string, and headers, revealing that no specific details have been extracted from the request yet.
The log indicates that the HTTP POST
request is received with a JSON payload. The original request body contains information about the book, including the title and author.
It then transforms this information, prepares an endpoint request to a specified Lambda function, and forwards the request.
Upon receiving the Lambda response, API Gateway performs transformations before sending the final response to the client. This involves mapping the Lambda response to a structured API response.
Step 5: Configure Method and Integration Request/Response
In Amazon API Gateway, both Integration Response and Method Response are components for controlling and defining the structure of API responses. They control how the request and response data is handled at various stages of the API request lifecycle, especially in the context of Lambda Non-Proxy Integration in API Gateway.
Method Request/Response: The Method Request and Response phase captures information from the incoming request and defines the structure of the final API response that is sent to the client.
Integration Request/Response: The Integration Request phase transforms the method request into the format expected by the backend Lambda function. Followed by transforming the Lambda function’s response before sending it back to the client.
To demonstrate this process, let’s consider a URL path /books?id={id}
where id
is a URL query string, and we want to pass this from the client to our Lambda function. Here’s how we can achieve this:
In the API Gateway console, navigate to our API and select the HTTP GET
method. Click on the Method Request
tab and click the Edit
button.
Under URL Query String Parameters
, add a query parameter named id
.
Next, open the Integration Request
tab and click Edit
. Under Mapping Templates
, add a new template for application/json
(or the appropriate content type). In the template, map the query string parameter to a event
body.
For example, if the incoming request has a URL like /books?id=123
, this template will transform it into the following JSON structure in the request body:
{
"id": "123"
}
Now, navigate to the Integration Response
tab, edit the integration response for HTTP status code 200
, and add a new mapping template to transform the Lambda output to the desired response format.
Go to the Models
section and create a new model call BookResponseModel
. Define the model structure as follows:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "BookResponseModel",
"type": "object",
"properties": {
"id": { "type": "integer" },
"title": { "type": "string" },
"author": { "type": "string" }
},
"required": ["id", "title", "author"]
}
Go back to the Method Response
in the resource. For the 200 HTTP status code, select the BookResponseModel
as the response model for application/json
.
Test the GET request by inputting the request body as follows:
Click the Test
button to simulate the API request using the configured test event. Review the results, including the request sent and the response received.
Step 6: Handle Lambda errors in API Gateway
To handle a 404
not found error, navigate to the Integration Response
and update the mapping template as the code snippet below:
#set($inputRoot = $input.path('$'))
#if($inputRoot.statusCode == 404)
{
"error": {
"statusCode": 404,
"message": "Resource Not Found"
}
}
#else
{
"id": $util.parseJson($inputRoot.body).bookId,
"title": $util.parseJson($inputRoot.body).bookTitle,
"author": $util.parseJson($inputRoot.body).authorName
}
#end
Click the Test
button to simulate the API request using the configured test event. Observe the results, including the request sent and the response received.
Note that the method completed with status: 200
. Returning a 200 OK status when a resource is not found can mislead users and make it difficult for them to understand the API’s behavior.
For Lambda custom integrations, you must map errors returned by Lambda in the integration response to standard HTTP error responses for your clients. Otherwise, Lambda errors are returned as
https://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html200 OK
responses by default and the result is not intuitive for your API users.
The Lambda proxy integration requires Lambda to return an output in the following format:
{
"isBase64Encoded" : "boolean",
"statusCode": "number",
"headers": { ... },
"body": "JSON string"
}
With Lambda non-proxy integration, to return the desired HTTP status code, rely on integration response to extract the error based on HTTP status regex
.
To transform the 404 not found error using integration response, navigate to the Method Response
, create a new method response with HTTP code 404. Then, go to the Integration Response
tab and create a new integration response:
Our Lambda function should actively raise an exception when it encounters a book not found scenario. When the Lambda function returns an error message, the HTTP status regex will be used to identify the appropriate HTTP status code.
if not book_id == "123":
raise Exception('Not Found')
Remove the else part of the Mapping Template
for the 200 responses:
#set($inputRoot = $input.path('$'))
#if($inputRoot.statusCode == 200)
{
"id": $util.parseJson($inputRoot.body).bookId,
"title": $util.parseJson($inputRoot.body).bookTitle,
"author": $util.parseJson($inputRoot.body).authorName
}
#end
Test the API again. We should see the method completed with status: 404
, indicating that the resource was not found.
Conclusion
In this tutorial, we’ve covered the process of creating a simple CRUD API for managing a list of books using AWS API Gateway and Lambda functions. By following the steps outlined in this guide, we’ve learned how to set up an API in API Gateway, deploy Lambda functions for handling various CRUD operations, and configure method request/response and integration request/response to shape the behaviour of our API.
Share this content:
Leave a Comment