A Lambda handler is an adapter between AWS and an application. When it also owns business rules, persistence, and notifications, those rules become difficult to reuse from a queue worker or test without AWS-shaped input.
I give handlers four jobs:
- translate and validate the incoming request;
- read authenticated context;
- construct concrete dependencies;
- map the application result to HTTP.
const users = new MongoUserRepository(getDatabase());
const emails = new SesEmailGateway(getSesClient());
const register = registerUser({ users, emails });
export const handler = withHttp(async (request) => {
const input = registrationSchema.parse(request.body);
const user = await register(input);
return {
statusCode: 201,
body: JSON.stringify({ id: user.id }),
};
});
The handler reads almost like configuration. Registration rules live in registerUser, email delivery lives behind a gateway, and MongoDB queries live in the repository.
Reuse expensive clients#
AWS recommends initializing SDK clients and database connection machinery outside the handler so warm invocations can reuse them:
const documentClient = DynamoDBDocumentClient.from(new DynamoDBClient({}));
export const handler = withHttp(async (request) => {
// use the reused client through an adapter
});
Do not store request-specific data in module globals. Warm execution environments are reused, and state can leak between invocations.
Dependency injection does not require a container#
Plain constructor arguments and factory functions are often enough. A dependency-injection framework becomes useful only when the object graph is large enough to justify its lifecycle and registration model.
Explicit wiring has an important benefit: opening the handler shows which infrastructure a function can touch.
Know what to test#
Most decisions belong to use-case tests. Handler tests should cover the adapter contract: malformed input, authentication context, status codes, and serialization. Do not repeat every business test through API Gateway events.
Thin handlers do not reduce a Lambda to “just glue.” They make the glue honest. AWS-specific work stays visible at the entry point, while the behavior that matters remains portable TypeScript.