Serverless with Lambda and API Gateway
So far every service has meant managing a running server (EC2 instances, at minimum). Lambda flips that: you upload a function, and AWS runs it only when triggered, you never provision or manage a server at all.
Writing a Lambda function
exports.handler = async (event) => {
const name = event.queryStringParameters?.name ?? 'world';
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: `Hello, ${name}!` }),
};
};
A Lambda function is just a handler function with a defined input (event) and output shape, AWS packages it, runs it in an isolated environment, and you're billed per invocation and per millisecond of execution time, nothing while it's idle.
Triggers
Lambda functions don't run on their own, something has to invoke them:
| Trigger | Use case |
|---|---|
| API Gateway | Turn a function into an HTTP endpoint |
| S3 event | Run a function whenever a file is uploaded to a bucket |
| Scheduled (EventBridge) | Run a function on a cron-like schedule |
| SQS queue | Process messages from a queue as they arrive |
API Gateway: HTTP in front of Lambda
API Gateway is what turns a Lambda function into a real REST API with a public URL, handling routing, request validation, throttling, and API keys in front of your function.
GET /hello → API Gateway → Lambda (handler above) → response
You define routes in API Gateway, each one maps to a Lambda function (or another backend), API Gateway handles the HTTP layer, your function just receives a plain JavaScript object describing the request.
Cold starts
The tradeoff for not managing servers: the first invocation after a period of inactivity has to initialize a fresh execution environment before running your code, called a cold start, adding noticeable latency (tens to hundreds of milliseconds, sometimes more for larger runtimes/dependencies). Subsequent invocations reuse a warm environment and are fast.
TIP
If cold-start latency matters for your use case (a user-facing API with strict latency requirements), keep your function's dependencies small, and consider provisioned concurrency, which keeps a set number of execution environments pre-warmed at all times, for an additional cost.