A serverless REST API built with AWS Lambda and API Gateway doesn’t actually eliminate servers; it just moves the burden of managing them to AWS, allowing you to focus on your application code.
Let’s see this in action. Imagine we want to create a simple API to fetch user data.
First, we’ll set up an AWS Lambda function. This function will contain our application logic.
# lambda_function.py
import json
import boto3
def lambda_handler(event, context):
user_id = event['queryStringParameters']['userId']
# In a real scenario, you'd fetch this from a database like DynamoDB
user_data = {
"userId": user_id,
"name": "Jane Doe",
"email": "jane.doe@example.com"
}
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps(user_data)
}
This Python code defines a lambda_handler function that takes event and context objects. It expects a userId query parameter, simulates fetching user data, and returns it as a JSON response with a 200 OK status.
Next, we configure API Gateway to act as the front door for our Lambda function. We’ll create a REST API, define a resource (e.g., /users), and a method (e.g., GET).
In API Gateway, under your new REST API, navigate to "Resources".
Click "Actions" -> "Create Resource".
For "Resource Name", enter users.
Click "Create Resource".
Now, with the /users resource selected, click "Actions" -> "Create Method".
From the dropdown, select GET.
Click the checkmark.
For the GET /users method, select "Lambda Function" as the integration type.
In the "Lambda Function" field, type the name of your Lambda function (e.g., getUserFunction).
Click "Save".
API Gateway will ask for permission to invoke your Lambda function. Click "OK".
This connects the HTTP GET request to /users to our getUserFunction Lambda. API Gateway will translate the incoming HTTP request into an event object that Lambda understands, and then translate the Lambda function’s return value back into an HTTP response.
To make this accessible, we need to deploy the API.
In API Gateway, with your REST API selected, click "Actions" -> "Deploy API".
For "Deployment stage", select "[New Stage]".
Enter a "Stage name", for example, prod.
Click "Deploy".
API Gateway will then provide an "Invoke URL". This is the public endpoint for your API. It will look something like https://abcdef123.execute-api.us-east-1.amazonaws.com/prod.
You can now test your API using tools like curl or Postman.
curl "https://abcdef123.execute-api.us-east-1.amazonaws.com/prod/users?userId=12345"
This command sends a GET request to your deployed API endpoint, including the userId query parameter. The API Gateway routes this to your Lambda function, which processes it and returns the simulated user data.
The core problem this pattern solves is the operational overhead of managing web servers, load balancers, and scaling infrastructure. API Gateway handles request routing, authentication, authorization, throttling, and caching. Lambda handles the execution of your application code, scaling automatically based on demand.
The event object passed to your Lambda function is a JSON representation of the incoming HTTP request. For a GET request with query parameters, it will contain a queryStringParameters key. For POST requests with a body, it will have a body key, which is a string that you’ll typically need to parse using json.loads(). The context object provides runtime information about the Lambda invocation.
The response structure from Lambda is also specific. It needs to include statusCode, headers, and body. The body must be a string, which is why we use json.dumps() to convert our Python dictionary into a JSON string.
When you configure the integration between API Gateway and Lambda, you choose between "Lambda Proxy Integration" and "Lambda Custom Integration". The "Lambda Proxy Integration" (which is the default and recommended for most use cases) passes the raw request data to Lambda and expects a specific response format back. This is what we’ve used here. A "Lambda Custom Integration" offers more control but requires more configuration to map request and response parameters.
The most surprising thing about this setup is how easily API Gateway can manage authorization. You can integrate with AWS Cognito User Pools, IAM roles, or even custom Lambda authorizers to secure your endpoints without writing any authorization logic in your Lambda function itself. For example, a Lambda authorizer can be a separate Lambda function that inspects the incoming token and returns an IAM policy that either allows or denies access to the requested API resource.
The next concept to explore is managing state and data persistence, as Lambda functions are stateless by design.