lambda-response-kit
v1.0.0
Published
A lightweight, type-safe utility library for standardizing AWS Lambda HTTP responses.
Maintainers
Readme
lambda-response-kit
A lightweight, type-safe utility library for standardizing AWS Lambda HTTP responses. Built for Node.js with TypeScript-first design.
Features
✅ Type-Safe: Full TypeScript support with strict types
✅ Zero Dependencies: Ships with no external dependencies
✅ CORS Built-In: Automatic CORS header handling
✅ Middleware Support: Composable middleware for cross-cutting concerns
✅ Error Handling: Standardized error responses with proper status codes
✅ Validation Ready: Built-in validation and error serialization
✅ Production Ready: Tested, documented, and optimized
Installation
npm install lambda-response-kitQuick Start
import { createResponseBuilder } from 'lambda-response-kit';
export const handler = async (event, context) => {
const response = createResponseBuilder();
try {
const data = await processRequest(event.body);
return response.success(data, 201);
} catch (error) {
return response.error(error, 400);
}
};Core API
ResponseBuilder
The ResponseBuilder class provides methods for constructing standardized Lambda responses.
success(data, statusCode)
Returns a successful response with data.
const response = builder.success({ id: 1, name: 'John' }, 201);
// Returns:
// {
// statusCode: 201,
// headers: { /* CORS + content-type */ },
// body: JSON.stringify({
// success: true,
// data: { id: 1, name: 'John' },
// timestamp: '2024-01-15T10:30:00Z',
// requestId: 'req-123'
// })
// }error(error, statusCode, message?)
Returns an error response with standardized format.
// With Error object
const error = new Error('User not found');
response.error(error, 404);
// With string
response.error('NotFoundError', 404, 'User with ID 123 not found');Convenience Methods
// 401 Unauthorized
response.unauthorized('Invalid credentials');
// 403 Forbidden
response.forbidden('Access denied');
// 404 Not Found
response.notFound('Resource not found');
// 400 Bad Request
response.badRequest('Missing required fields', { field: 'email' });
// 422 Validation Error
response.validationError({
email: ['Email is required', 'Must be valid format'],
password: ['Must be at least 8 characters'],
});
// 500 Server Error
response.serverError('Database connection failed');
// 204 No Content
response.noContent();
// 302 Redirect
response.redirect('https://example.com/new-path', 301);custom(statusCode, body, headers?)
Build a completely custom response.
response.custom(202, { status: 'processing' }, {
'X-Custom-Header': 'value',
});Context Management
Set Lambda context to include request IDs in responses:
export const handler = async (event, context) => {
const response = createResponseBuilder();
response.setContext(context);
return response.success({ id: 1 });
// Now responses include requestId from context
};Configuration
Customize default behavior when creating a builder:
const response = createResponseBuilder({
// Enable/disable CORS headers (default: true)
includeCorHeaders: true,
// Set allowed origins (default: '*')
allowOrigins: ['https://example.com', 'https://app.example.com'],
// Include timestamp in responses (default: true)
includeTimestamp: true,
// Include request ID in responses (default: true)
includeRequestId: true,
// Add custom default headers to all responses
defaultHeaders: {
'X-API-Version': '1.0.0',
'Cache-Control': 'no-cache',
},
});Middleware
The library includes a middleware composition system for building reusable request/response handlers.
MiddlewareChain
import {
createMiddlewareChain,
bodyParsingMiddleware,
authenticationMiddleware,
errorHandlingMiddleware,
corsPreflightMiddleware,
} from 'lambda-response-kit';
export const handler = (event, context) => {
const chain = createMiddlewareChain();
return chain
.use(corsPreflightMiddleware())
.use(errorHandlingMiddleware())
.use(bodyParsingMiddleware())
.use(authenticationMiddleware(verifyToken))
.execute(yourHandler, event, context);
};
async function yourHandler(event, context) {
// event.body is already parsed
// Authentication is verified
// Errors are caught and formatted
return { success: true, data: event.body };
}Built-in Middleware
corsPreflightMiddleware()
Automatically handles OPTIONS requests for CORS preflight.
chain.use(corsPreflightMiddleware());bodyParsingMiddleware()
Parses JSON request body, returns 400 if invalid.
chain.use(bodyParsingMiddleware());
// Now event.body is automatically parsedauthenticationMiddleware(verify)
Validates Bearer tokens using your verification function.
chain.use(
authenticationMiddleware(async (token) => {
// Verify token, return boolean
const decoded = await verifyJWT(token);
return !!decoded;
})
);
// Unauthorized requests get 401 responsevalidationMiddleware(validator)
Validates incoming requests using custom logic.
chain.use(
validationMiddleware((event) => {
if (!event.body?.email) {
return {
valid: false,
errors: { email: ['Email is required'] },
};
}
return { valid: true };
})
);loggingMiddleware(level?)
Logs request/response with duration and request ID.
chain.use(loggingMiddleware('info')); // or 'debug'
// Logs: [request-id] Incoming request: { method, path, source }
// Logs: [request-id] Request completed in 125mserrorHandlingMiddleware()
Catches all errors and returns appropriate HTTP responses.
chain.use(errorHandlingMiddleware());
// Automatically converts errors to 400/500 responses
// Detects SyntaxError → 400
// Detects timeout → 503Custom Middleware
Create your own middleware:
const customMiddleware = async (event, context, next) => {
// Before handler
console.log('Processing request:', event.path);
try {
const result = await next();
// After handler (success)
console.log('Request successful');
return result;
} catch (error) {
// After handler (error)
console.error('Request failed:', error);
throw error;
}
};
chain.use(customMiddleware);Response Format
Success Response
{
"success": true,
"data": { /* your data */ },
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "aws-request-id"
}Error Response
{
"error": "ErrorType",
"message": "Description of what went wrong",
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "aws-request-id"
}Validation Error Response
{
"error": "ValidationError",
"message": "Validation failed",
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "aws-request-id",
"errors": {
"email": ["Email is required", "Must be valid format"],
"password": ["Must be at least 8 characters"]
}
}Complete Examples
Simple CRUD Handler
import { createResponseBuilder, HttpStatus } from 'lambda-response-kit';
export const createUser = async (event, context) => {
const builder = createResponseBuilder();
builder.setContext(context);
try {
const body = JSON.parse(event.body);
if (!body.email || !body.name) {
return builder.validationError({
email: body.email ? [] : ['Email is required'],
name: body.name ? [] : ['Name is required'],
});
}
const user = await db.users.create(body);
return builder.success(user, HttpStatus.CREATED);
} catch (error) {
return builder.error(error, 500);
}
};With Middleware Chain
import {
createMiddlewareChain,
bodyParsingMiddleware,
authenticationMiddleware,
loggingMiddleware,
errorHandlingMiddleware,
} from 'lambda-response-kit';
export const handler = (event, context) => {
const chain = createMiddlewareChain();
return chain
.use(loggingMiddleware())
.use(errorHandlingMiddleware())
.use(bodyParsingMiddleware())
.use(authenticationMiddleware(verifyToken))
.execute(createUser, event, context);
};
async function createUser(event, context) {
const { email, name } = event.body;
// Validation
const errors: Record<string, string[]> = {};
if (!email) errors.email = ['Email is required'];
if (!name) errors.name = ['Name is required'];
if (Object.keys(errors).length > 0) {
throw new ValidationError('Invalid input', errors);
}
// Create and return
const user = await db.users.create({ email, name });
return { user, statusCode: 201 };
}Error Handling Pattern
class ValidationError extends Error {
constructor(message: string, public errors: Record<string, string[]>) {
super(message);
this.name = 'ValidationError';
}
}
export const handler = async (event, context) => {
const builder = createResponseBuilder();
builder.setContext(context);
try {
const result = await processRequest(event);
return builder.success(result);
} catch (error) {
if (error instanceof ValidationError) {
return builder.validationError(error.errors);
}
if (error instanceof NotFoundError) {
return builder.notFound(error.message);
}
console.error('Unhandled error:', error);
return builder.serverError();
}
};Testing
All responses can be easily tested:
describe('User API', () => {
let builder: ResponseBuilder;
beforeEach(() => {
builder = new ResponseBuilder();
builder.setContext({ requestId: 'test-123' });
});
it('should create user successfully', async () => {
const response = builder.success(
{ id: 1, email: '[email protected]' },
201
);
expect(response.statusCode).toBe(201);
const body = JSON.parse(response.body);
expect(body.data.email).toBe('[email protected]');
});
it('should return validation error', () => {
const response = builder.validationError({
email: ['Email is required'],
});
expect(response.statusCode).toBe(422);
const body = JSON.parse(response.body);
expect(body.errors.email[0]).toBe('Email is required');
});
});Architecture Patterns
Layered Handler Pattern
const handler = (event, context) =>
createMiddlewareChain()
.use(corsPreflightMiddleware())
.use(bodyParsingMiddleware())
.use(authenticationMiddleware(verifyJWT))
.use(validationMiddleware(validateRequest))
.use(loggingMiddleware())
.execute(businessLogic, event, context);Error-First Middleware
const chain = createMiddlewareChain();
chain.use(errorHandlingMiddleware()); // First!
chain.use(loggingMiddleware()); // Then logging
chain.use(authenticationMiddleware()); // Then authRequest/Response Validation
chain
.use(
bodyParsingMiddleware()
)
.use(
validationMiddleware(validateRequestBody)
)
// Handler runs after validation
.execute(handler, event, context);Type Safety
Full TypeScript support with strict types:
import {
LambdaResponse,
LambdaContext,
SuccessBody,
ErrorBody
} from 'lambda-response-kit';
const handler = async (
event: any,
context: LambdaContext
): Promise<LambdaResponse> => {
const builder = createResponseBuilder();
return builder.success({ id: 1 });
};Performance
- Minimal overhead: No external dependencies
- Lightweight: ~5KB gzipped
- Fast parsing: Native JSON
- Efficient middleware: Zero-allocation dispatch
Contributing
Contributions welcome! Please ensure:
- All tests pass:
npm test - Code is formatted:
npm run format - Linting passes:
npm run lint - Coverage maintained:
npm run test:coverage
License
MIT
