Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
342 changes: 321 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,171 @@ exports.handler = async (event, context) => {

For a full tutorial see [How To: Build a Serverless API with Serverless, AWS Lambda and Lambda API](https://www.jeremydaly.com/build-serverless-api-serverless-aws-lambda-lambda-api/).

## TypeScript Support

Lambda API includes comprehensive TypeScript definitions out of the box. You can leverage type safety across your entire API:

```typescript
import {
API,
Request,
Response,
SourceAgnosticHandler,
SourceAgnosticMiddleware,
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TypeScript Support code example in the README uses APIGatewayContext at line 103, but this type is not included in the import statement shown at lines 42-50. Users copying this example would get a TypeScript error. The import block should include APIGatewayContext (and potentially ALBContext for completeness).

Suggested change
SourceAgnosticMiddleware,
SourceAgnosticMiddleware,
APIGatewayContext,
ALBContext,

Copilot uses AI. Check for mistakes.
isApiGatewayContext,
isAlbContext,
} from 'lambda-api';

// Define your response type
interface UserResponse {
id: string;
name: string;
email: string;
}

// Create a typed API instance
const api = new API();

// Source-agnostic handler that works with any Lambda trigger
const handler: SourceAgnosticHandler<UserResponse> = (req, res) => {
// Common properties are always available
console.log(req.method, req.path);

// Type-safe access to source-specific features
if (isApiGatewayContext(req.requestContext)) {
console.log(req.requestContext.identity);
} else if (isAlbContext(req.requestContext)) {
console.log(req.requestContext.elb);
}

res.json({
id: '1',
name: 'John',
email: 'john@example.com',
});
};

// Source-agnostic middleware
const middleware: SourceAgnosticMiddleware = (req, res, next) => {
// Works with any Lambda trigger
console.log(`${req.method} ${req.path}`);
next();
};

// Use with API methods
api.get('/users', middleware, handler);

// For source-specific handlers, you can specify the context type
interface UserQuery {
fields: string;
}
interface UserParams {
id: string;
}
interface UserBody {
name: string;
email: string;
}
Comment on lines +92 to +101
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UserQuery and UserParams interfaces defined in the README TypeScript example (lines 92-101) are not compatible with the generic type constraint TQuery extends Record<string, string | undefined>. The interfaces declare non-optional string properties (fields: string, id: string), but the constraint requires values to be string | undefined. Using these definitions at line 103 would cause a TypeScript compile error. The properties should be declared as optional (e.g., fields?: string) or the interfaces should extend Record<string, string | undefined>, consistent with how they are defined in index.test-d.ts.

Copilot uses AI. Check for mistakes.

api.post<UserResponse, APIGatewayContext, UserQuery, UserParams, UserBody>(
'/users',
(req, res) => {
// Full type safety for:
req.query.fields; // UserQuery
req.params.id; // UserParams
req.body.name; // UserBody
req.requestContext; // APIGatewayContext

res.json({
id: '1',
name: req.body.name,
email: req.body.email,
});
}
);
```

Key TypeScript Features:

- Source-agnostic types that work with any Lambda trigger
- Type guards for safe context type checking
- Full type inference for request and response objects
- Generic type parameters for response types
- Support for API Gateway and ALB contexts
- Type-safe query parameters, path parameters, and request body
- Middleware and error handler type definitions
- Automatic type inference for all HTTP methods
- Type safety for cookies, headers, and other API features

## Type Guards

Lambda API provides type guards to safely work with different request sources:

```typescript
import {
isApiGatewayContext,
isApiGatewayV2Context,
isAlbContext,
isApiGatewayRequest,
isApiGatewayV2Request,
isAlbRequest,
} from 'lambda-api';

// Check request context type
if (isApiGatewayContext(req.requestContext)) {
// TypeScript knows this is APIGatewayContext
console.log(req.requestContext.identity);
}

// Check entire request type
if (isApiGatewayRequest(req)) {
// TypeScript knows this is Request<APIGatewayContext>
console.log(req.requestContext.identity);
}
```

## Handling Multiple Request Sources

Lambda API provides type-safe support for different AWS Lambda triggers. You can write source-specific handlers or use source-agnostic handlers that work with any trigger:

```typescript
import {
isApiGatewayContext,
isApiGatewayV2Context,
isAlbContext,
SourceAgnosticHandler,
} from 'lambda-api';

// Source-specific handler
api.get<Response, APIGatewayContext>('/api-gateway', (req, res) => {
console.log(req.requestContext.identity);
});

api.get<Response, ALBContext>('/alb', (req, res) => {
console.log(req.requestContext.elb);
});

// Source-agnostic handler (works with any trigger)
const handler: SourceAgnosticHandler = (req, res) => {
if (isApiGatewayContext(req.requestContext)) {
console.log(req.requestContext.identity);
} else if (isAlbContext(req.requestContext)) {
console.log(req.requestContext.elb);
}

res.json({ status: 'ok' });
};

api.get('/any', handler);
```

Key features for handling multiple sources:

- Type guards for safe context type checking
- Source-agnostic types that work with any trigger
- Full type safety for source-specific properties
- Automatic payload format detection

## Why Another Web Framework?

Express.js, Fastify, Koa, Restify, and Hapi are just a few of the many amazing web frameworks out there for Node.js. So why build yet another one when there are so many great options already? One word: **DEPENDENCIES**.
Expand Down Expand Up @@ -127,6 +292,7 @@ Whatever you decide is best for your use case, **Lambda API** is there to suppor
- [TypeScript Support](#typescript-support)
- [Contributions](#contributions)
- [Are you using Lambda API?](#are-you-using-lambda-api)
- [Handling Multiple Request Sources](#handling-multiple-request-sources)

## Installation

Expand Down Expand Up @@ -1463,7 +1629,7 @@ Lambda API automatically parses this information to create a normalized `REQUEST

## ALB Integration

AWS recently added support for Lambda functions as targets for Application Load Balancers. While the events from ALBs are similar to API Gateway, there are a number of differences that would require code changes based on implementation. Lambda API detects the event `interface` and automatically normalizes the `REQUEST` object. It also correctly formats the `RESPONSE` (supporting both multi-header and non-multi-header mode) for you. This allows you to call your Lambda function from API Gateway, ALB, or both, without requiring any code changes.
AWS supports Lambda functions as targets for Application Load Balancers. While the events from ALBs are similar to API Gateway, there are a number of differences that would require code changes based on implementation. Lambda API detects the event `interface` and automatically normalizes the `REQUEST` object. It also correctly formats the `RESPONSE` (supporting both multi-header and non-multi-header mode) for you. This allows you to call your Lambda function from API Gateway, ALB, or both, without requiring any code changes.

Please note that ALB events do not contain all of the same headers as API Gateway (such as `clientType`), but Lambda API provides defaults for seamless integration between the interfaces. ALB also automatically enables binary support, giving you the ability to serve images and other binary file types. Lambda API reads the `path` parameter supplied by the ALB event and uses that to route your requests. If you specify a wildcard in your listener rule, then all matching paths will be forwarded to your Lambda function. Lambda API's routing system can be used to process these routes just like with API Gateway. This includes static paths, parameterized paths, wildcards, middleware, etc.

Expand All @@ -1479,37 +1645,171 @@ Simply create a `{proxy+}` route that uses the `ANY` method and all requests wil

If you are using persistent connections in your function routes (such as AWS RDS or Elasticache), be sure to set `context.callbackWaitsForEmptyEventLoop = false;` in your main handler. This will allow the freezing of connections and will prevent Lambda from hanging on open connections. See [here](https://www.jeremydaly.com/reuse-database-connections-aws-lambda/) for more information.

## TypeScript Support
## Contributions

Contributions, ideas and bug reports are welcome and greatly appreciated. Please add [issues](https://github.com/jeremydaly/lambda-api/issues) for suggestions and bug reports or create a pull request.

## Are you using Lambda API?

If you're using Lambda API and finding it useful, hit me up on [Twitter](https://twitter.com/jeremy_daly) or email me at contact[at]jeremydaly.com. I'd love to hear your stories, ideas, and even your complaints!

An `index.d.ts` declaration file has been included for use with your TypeScript projects (thanks @hassankhan). Please feel free to make suggestions and contributions to keep this up-to-date with future releases.
## Type-Safe Middleware and Extensions

**TypeScript Example**
Lambda API provides full TypeScript support with type-safe middleware and request/response extensions. Here are the recommended patterns:

### Extending Request and Response Types

```typescript
// import AWS Lambda types
import { APIGatewayEvent, Context } from 'aws-lambda';
// import Lambda API default function
import createAPI from 'lambda-api';
declare module 'lambda-api' {
interface Request {
user?: {
id: string;
roles: string[];
email: string;
};
}
}

function hasUser(
req: Request
): req is Request & { user: { id: string; roles: string[]; email: string } } {
return 'user' in req && req.user !== undefined;
}

const authMiddleware: Middleware = (req, res, next) => {
req.user = {
id: '123',
roles: ['admin'],
email: 'user@example.com',
};
next();
};

api.get('/protected', (req, res) => {
if (hasUser(req)) {
const { id, roles, email } = req.user;
res.json({ message: `Hello ${email}` });
}
});
```

// instantiate framework
const api = createAPI();
### Response Extensions

// Define a route
api.get('/status', async (req, res) => {
return { status: 'ok' };
```typescript
declare module 'lambda-api' {
interface Response {
sendWithTimestamp?: (data: any) => void;
}
}

const responseEnhancer: Middleware = (req, res, next) => {
res.sendWithTimestamp = (data: any) => {
res.json({
...data,
timestamp: Date.now(),
});
};
next();
};

api.get('/users', (req, res) => {
res.sendWithTimestamp({ name: 'John' });
});
```

// Declare your Lambda handler
exports.run = async (event: APIGatewayEvent, context: Context) => {
// Run the request
return await api.run(event, context);
### Using Built-in Auth Property

```typescript
interface AuthInfo {
userId: string;
roles: string[];
type: 'Bearer' | 'Basic' | 'OAuth' | 'Digest' | 'none';
value: string | null;
}

function hasAuth(req: Request): req is Request & { auth: AuthInfo } {
return 'auth' in req && req.auth?.type !== undefined;
}

const authMiddleware: Middleware = (req, res, next) => {
req.auth = {
userId: '123',
roles: ['user'],
type: 'Bearer',
value: 'token123',
};
next();
};
```

## Contributions
### Type Safety Examples

Contributions, ideas and bug reports are welcome and greatly appreciated. Please add [issues](https://github.com/jeremydaly/lambda-api/issues) for suggestions and bug reports or create a pull request.
```typescript
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Type Safety Examples" code snippet uses UserType at line 1748, but this type is never defined in the code snippet or the surrounding context. This would cause a TypeScript compile error for anyone copying this example. It should be replaced with an actual type definition (e.g., defining interface UserType { id: string; roles: string[]; email: string; } above or using an existing type like UserResponse).

Suggested change
```typescript
```typescript
interface UserType {
id: string;
roles: string[];
email: string;
}

Copilot uses AI. Check for mistakes.
function hasUser(req: Request): req is Request & { user: UserType } {
return 'user' in req && req.user !== undefined;
}

interface QueryParams {
limit?: string;
offset?: string;
}

api.get<UserResponse, APIGatewayContext, QueryParams>('/users', (req, res) => {
const { limit, offset } = req.query;
res.json({
/* ... */
});
});

## Are you using Lambda API?
interface CreateUserBody {
name: string;
email: string;
}

api.post<UserResponse, APIGatewayContext, never, never, CreateUserBody>(
'/users',
(req, res) => {
const { name, email } = req.body;
res.json({
/* ... */
});
}
);

If you're using Lambda API and finding it useful, hit me up on [Twitter](https://twitter.com/jeremy_daly) or email me at contact[at]jeremydaly.com. I'd love to hear your stories, ideas, and even your complaints!
const withUser = <T>(handler: HandlerFunction<T>): HandlerFunction<T> => {
return (req, res) => {
if (!hasUser(req)) {
return res.status(401).json({ error: 'Unauthorized' });
}
return handler(req, res);
};
};

api.get('/protected', withUser(handler));
```

## Handling Multiple Request Sources

```typescript
import {
isApiGatewayContext,
isApiGatewayV2Context,
isAlbContext,
} from 'lambda-api';

api.get<Response, APIGatewayRequestContext>('/api-gateway', (req, res) => {
console.log(req.requestContext.identity);
});

api.get<Response, ALBRequestContext>('/alb', (req, res) => {
Comment on lines +1800 to +1804
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README's second "Handling Multiple Request Sources" section (near the end of the file) uses the non-existent type names APIGatewayRequestContext and ALBRequestContext as type parameters. The correct exported types are APIGatewayContext and ALBContext respectively. Additionally, Response is used as the first type parameter (the response body type), but Response is the response class, not a response body shape. The earlier section of the same README (around line 173) correctly uses APIGatewayContext and ALBContext, making this a clear inconsistency in the documentation.

Suggested change
api.get<Response, APIGatewayRequestContext>('/api-gateway', (req, res) => {
console.log(req.requestContext.identity);
});
api.get<Response, ALBRequestContext>('/alb', (req, res) => {
api.get<{ message: string }, APIGatewayContext>('/api-gateway', (req, res) => {
console.log(req.requestContext.identity);
});
api.get<{ message: string }, ALBContext>('/alb', (req, res) => {

Copilot uses AI. Check for mistakes.
console.log(req.requestContext.elb);
});

api.get('/any', (req, res) => {
if (isApiGatewayContext(req.requestContext)) {
console.log(req.requestContext.identity);
} else if (isAlbContext(req.requestContext)) {
console.log(req.requestContext.elb);
}
});
```
Comment on lines +1791 to +1815
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The section heading "## Handling Multiple Request Sources" appears twice in the README — at line 160 and line 1791. This creates a duplicate heading with conflicting content, where the TOC entry at line 295 can only link to one of them. The second occurrence (line 1791) only contains a short code snippet and duplicates content already present in the first. The duplicate section should be merged or removed to avoid confusion.

Copilot uses AI. Check for mistakes.
Loading