The Key Difference
Express (local): Server runs continuously, waiting for requests.
Lambda (cloud): Function runs only when called. No server to manage.
Understanding Lambda Handlers
A Lambda handler is just a function that AWS calls when a request comes in.
Basic structure:
exports.handler = async (event) => {
// event contains request data
// process the request
return {
statusCode: 200,
body: JSON.stringify({ message: "Hello" })
};
};
The event object: Contains HTTP request data (method, path, headers, body).
The response: Must have statusCode and body (as a string).
Option A: Minimal Lambda Handler (Recommended for Learning)
Let’s rewrite our API as a pure Lambda handler. This helps you understand how Lambda works.
Create a new file lambda-handler.js:
// In-memory storage
let notes = [
{ id: 1, text: 'Buy milk' },
{ id: 2, text: 'Call mom' }
];
let nextId = 3;
exports.handler = async (event) => {
// Parse the path and method from the event
const path = event.path || event.requestContext?.path || '';
const method = event.httpMethod || event.requestContext?.httpMethod || 'GET';
// Health check
if (path === '/health' && method === 'GET') {
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: 'ok',
timestamp: new Date().toISOString()
})
};
}
// Get all notes
if (path === '/notes' && method === 'GET') {
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ notes })
};
}
// Add a new note
if (path === '/notes' && method === 'POST') {
let body;
try {
body = typeof event.body === 'string' ? JSON.parse(event.body) : event.body;
} catch (e) {
return {
statusCode: 400,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ error: 'Invalid JSON' })
};
}
const { text } = body;
if (!text) {
return {
statusCode: 400,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ error: 'text is required' })
};
}
const newNote = {
id: nextId++,
text: text
};
notes.push(newNote);
return {
statusCode: 201,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newNote)
};
}
// 404 for unknown routes
return {
statusCode: 404,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ error: 'Not found' })
};
};
Understanding the Lambda Event
The event object structure depends on how API Gateway is configured. For HTTP APIs, it looks like:
{
"version": "2.0",
"routeKey": "GET /notes",
"rawPath": "/notes",
"rawQueryString": "",
"headers": {
"accept": "application/json",
"content-type": "application/json"
},
"requestContext": {
"http": {
"method": "GET",
"path": "/notes"
}
},
"body": null // or JSON string for POST
}
Our code handles both old and new event formats for compatibility.
Option B: Use aws-serverless-express (Optional)
If you want to keep using Express, you can use aws-serverless-express. This wraps your Express app so it works with Lambda.
Install:
npm install aws-serverless-express
Modify your code:
const express = require('express');
const awsServerlessExpress = require('aws-serverless-express');
const app = express();
app.use(express.json());
// ... your routes ...
// Export the handler
const server = awsServerlessExpress.createServer(app);
exports.handler = (event, context) => {
awsServerlessExpress.proxy(server, event, context);
};
Pros: Reuse existing Express code.
Cons: Adds complexity, larger package size, harder to debug.
For this tutorial: We’ll use Option A (pure Lambda handler) so you understand how Lambda works.
Side-by-Side Comparison
Express Version (Local)
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.listen(3000);
What happens:
- Server starts and waits
- When request comes, Express calls your route handler
- You use
res.json()to send response
Lambda Version (Cloud)
exports.handler = async (event) => {
if (event.path === '/health') {
return {
statusCode: 200,
body: JSON.stringify({ status: 'ok' })
};
}
};
What happens:
- No server running
- When request comes, AWS calls your handler
- You return a response object
- AWS sends it back to the client
Key Differences
| Express | Lambda |
|---|---|
app.listen() starts server | No server to start |
req and res objects | event object |
res.json() sends response | Return response object |
| Runs continuously | Runs on-demand |
| You manage the process | AWS manages execution |
Testing the Lambda Handler Locally
You can test Lambda handlers locally by creating a test event:
Create test-event.json:
{
"path": "/notes",
"httpMethod": "GET",
"headers": {},
"body": null
}
Create test-local.js:
const handler = require('./lambda-handler');
const event = require('./test-event.json');
handler.handler(event)
.then(response => {
console.log('Response:', JSON.stringify(response, null, 2));
})
.catch(error => {
console.error('Error:', error);
});
Run:
node test-local.js
This lets you test Lambda code without deploying.
Key Takeaways
Before deploying:
- Lambda handlers are just functions - No server, just code that runs on demand
- Event object contains request data - Path, method, headers, body
- Response must be an object - With
statusCodeandbody(as string) - You can test locally - Create test events to debug
- Pure Lambda vs Express wrapper - Pure Lambda is simpler to understand
What’s Next?
In the next page, we’ll package this code and deploy it to AWS. You’ll create a Lambda function, upload the code, and connect it to API Gateway.