How to deploy a serverless REST API on AWS Lambda
Deploy a Node.js REST API on AWS Lambda behind API Gateway using Terraform. Covers handler code, packaging, IaC, and verification through CloudWatch.
Serverless functions let you run code without managing servers. AWS Lambda runs your handler in response to events and bills per millisecond of execution. Paired with Amazon API Gateway, it becomes a fully managed REST API that scales from zero to thousands of requests automatically.
This tutorial deploys a small Node.js API on Lambda fronted by API Gateway, provisioned with Terraform so the whole stack is reproducible.
Prerequisites
- An AWS account and credentials configured (
aws configure). - Node.js 20 or later.
- Terraform 1.6 or later.
Steps
1. Write the handler
Create index.mjs:
export const handler = async (event) => {
const name = event.queryStringParameters?.name ?? "world";
return {
statusCode: 200,
headers: { "content-type": "application/json" },
body: JSON.stringify({ message: `hello ${name}` })
};
};
Lambda passes the HTTP request as event and expects a response object with statusCode, headers, and body.
2. Package dependencies
For a zero-dependency function, zip the source:
zip function.zip index.mjs
If you add npm packages, run npm ci --omit=dev and include node_modules in the archive.
3. Define the Lambda in Terraform
resource "aws_lambda_function" "api" {
function_name = "hello-api"
runtime = "nodejs20.x"
handler = "index.handler"
filename = "function.zip"
source_code_hash = filebase64sha256("function.zip")
role = aws_iam_role.lambda.arn
}
The source_code_hash ensures Terraform redeploys when the code changes.
4. Add API Gateway
Use an HTTP API, which is cheaper and simpler than the REST API type:
resource "aws_apigatewayv2_api" "http" {
name = "hello-http"
protocol_type = "HTTP"
}
resource "aws_apigatewayv2_integration" "lambda" {
api_id = aws_apigatewayv2_api.http.id
integration_type = "AWS_PROXY"
integration_uri = aws_lambda_function.api.invoke_arn
payload_format_version = "2.0"
}
Add a route and a stage, and grant API Gateway permission to invoke the function with an aws_lambda_permission resource.
5. Apply and deploy
terraform init
terraform apply
Terraform prints the invoke URL as an output. Capture it for the next step.
6. Test the endpoint
curl "$(terraform output -raw api_url)?name=ada"
You should see {"message":"hello ada"}.
Verification
Check CloudWatch Logs for the function's log group to confirm invocations. A cold start shows an INIT_START line; warm invocations skip it. Send a few requests and confirm latency drops after the first.
Next Steps
Add a custom domain with API Gateway and ACM, attach least-privilege IAM, and set reserved concurrency to cap cost. Move secrets into AWS Secrets Manager rather than environment variables.
Prerequisites
- AWS account with IAM access
- Node.js installed
- Terraform CLI installed
Steps
- 1Write the handler
- 2Package dependencies
- 3Define the Lambda in Terraform
- 4Add API Gateway
- 5Apply and deploy
- 6Test the endpoint