Why We Built Zapix: Express-style Routing for AWS Lambda
The story behind Zapix — how the frustration of writing boilerplate Lambda handlers led us to build a tiny, zero-config routing library with the DX you already love.
Every time I started a new AWS Lambda project, the routine was the same: write a giant switch-case on event.httpMethod + event.path, manually parse JSON bodies, forget to set Content-Type headers, reinvent 404 handling, and paste the same middleware boilerplate from the last project. Sound familiar?
Zapix was born out of that frustration. The goal was simple: bring the Express.js developer experience to serverless Lambda — without sacrificing cold-start performance or adding a mountain of dependencies.
The problem with Lambda handlers in the wild
Most Lambda HTTP handlers end up looking something like this:
export async function handler(event: APIGatewayProxyEvent) {
if (event.httpMethod === 'GET' && event.path === '/users') {
const users = await getUsers();
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(users),
};
}
if (event.httpMethod === 'POST' && event.path === '/users') {
const body = JSON.parse(event.body ?? '{}');
// validate, save, respond...
}
return { statusCode: 404, body: 'Not found' };
}This is brittle, hard to test, and impossible to scale. Add a few more routes and you have a 200-line function nobody wants to touch.
The Zapix approach
We took inspiration from Express.js — arguably the most ergonomic HTTP routing API ever designed — and built a minimal adapter that speaks the Lambda event/response protocol natively:
import { Zapix } from 'zapix';
import { handler } from 'zapix/aws';
const app = Zapix();
app.get('/users', async (req, res) => {
const users = await getUsers();
res.json(users);
});
app.post('/users', async (req, res) => {
const body = req.body;
const user = await createUser(body);
res.status(201).json(user);
});
export const main = handler(app);Design decisions we made deliberately
- Zero dependencies — No bloated node_modules subtree slowing down your cold start. Zapix has zero runtime dependencies.
- < 5KB bundle — Small enough to fit comfortably in a Lambda layer or inline in any deployment package.
- TypeScript first — Full type inference on req, res, and route params. No @types/* packages needed.
- Zero config — Import, route, export. Done. No app.listen(), no port management, no server lifecycle.
- Chainable middleware — app.use() works exactly the way you expect it to.
What's next
We just released v1.0.0-beta.1. The API is stable and we're using it in production. Coming up: route-level caching hints, built-in validation helpers, and a companion CDK/SAM construct for instant deployment.
If Zapix solves a real problem for you, give us a star on GitHub — it helps more developers find the project.
Try Zapix in your next Lambda project
Zero config. TypeScript first. Under 5KB.