API Handlers
defineApiHandler creates Lambda handlers for API Gateway endpoints with built-in auth, body validation, and response schema enforcement. Import from @mantleframework/validation.
Pattern
import { buildValidatedResponse } from '@mantleframework/core'
import { defineApiHandler, z } from '@mantleframework/validation'
const ResponseSchema = z.object({ items: z.array(z.string()) })
const api = defineApiHandler({ auth: 'bearer', operationName: 'ListItems' })
export const handler = api(async ({ context }) => {
return buildValidatedResponse(context, 200, { items: [] }, ResponseSchema)
})Options
| Option | Type | Default | Description |
|---|---|---|---|
schema | z.ZodSchema<TBody> | -- | Zod schema for request body validation |
auth | 'bearer' | 'none' | 'none' | Auth mode |
responseSchema | z.ZodSchema | -- | Response schema (optional, deprecated — pass to buildValidatedResponse instead) |
operationName | string | function name | Name for metrics and tracing |
openapi | { summary?, tags?, description? } | -- | OpenAPI metadata for generated spec |
Handler Params
When no schema is provided (ApiHandlerParams):
interface ApiHandlerParams {
event: APIGatewayProxyEvent
context: Context
metadata: WrapperMetadata // { traceId, correlationId }
}When a schema is provided (ValidatedApiParams<TBody>):
interface ValidatedApiParams<TBody> {
event: APIGatewayProxyEvent
context: Context
metadata: WrapperMetadata
body: TBody // Validated and typed request body
}Auth
Set auth: 'bearer' to require a static bearer token. The token is validated from the Authorization header before your handler runs. Unauthenticated requests receive a 401 response automatically.
For health checks, webhooks, or public endpoints omit auth (defaults to 'none'):
const api = defineApiHandler({ operationName: 'HealthCheck' })
export const handler = api(async ({ context }) => {
return buildResponse(context, 200, { status: 'ok' })
})Body Validation
Pass a Zod schema to schema to validate the request body. The validated value is available as body in the handler params, fully typed.
const SyncPayloadSchema = z.object({
items: z.array(z.object({ id: z.uuid(), value: z.number() })),
deviceId: z.uuid(),
})
const api = defineApiHandler({ schema: SyncPayloadSchema, auth: 'bearer', operationName: 'SyncData' })
export const handler = api(async ({ context, body }) => {
// body is typed as { items: { id: string; value: number }[]; deviceId: string }
const count = await DataQueries.upsertBatch(body.items, body.deviceId)
return buildValidatedResponse(context, 200, { synced: count }, ResponseSchema)
})When validation fails, the handler returns a 400 automatically:
{
"message": "Bad Request",
"errors": {
"items": ["Required"],
"deviceId": ["Invalid uuid"]
}
}OpenAPI Metadata
Add openapi to include the handler in the generated OpenAPI spec:
const api = defineApiHandler({
auth: 'bearer',
openapi: {
summary: 'Sync data items',
tags: ['data'],
},
})Response Helpers
buildValidatedResponse
Validates the response body against a Zod schema before returning. Recommended for all API responses.
import { buildValidatedResponse } from '@mantleframework/core'
return buildValidatedResponse(context, 200, { items }, ResponseSchema)buildResponse
Returns an API Gateway response without schema validation. Use for simple responses or 204 No Content.
import { buildResponse } from '@mantleframework/core'
return buildResponse(context, 204)
return buildResponse(context, 200, { message: 'ok' })Both helpers add correlation headers to the response automatically.
File-System Routing
API handler filenames follow <name>.<method>.ts under src/lambdas/api/:
| File | Route | Method | Lambda Name |
|---|---|---|---|
api/sync.post.ts | /sync | POST | Sync |
api/users/index.get.ts | /users | GET | UsersGet |
api/users/profile.get.ts | /users/profile | GET | UsersProfile |
See Also
- Handler Patterns — overview of all handler types
- File-system routing reference