Overview
Express Zod API allows you to customize which request properties are combined into the input that gets validated and made available to your endpoints and middlewares. This gives you fine-grained control over where input data comes from.
Default Configuration
The framework provides sensible defaults for each HTTP method:
import { createConfig } from "express-zod-api";
const config = createConfig({
inputSources: {
get: ["query", "params"],
post: ["body", "params", "files"],
put: ["body", "params"],
patch: ["body", "params"],
delete: ["query", "params"],
},
});
The order matters! Each item in the array has higher priority than the previous one. Later sources can override values from earlier sources.
Available Sources
You can include any of these request properties:
query - Query string parameters
body - Request body (parsed JSON)
params - Path parameters (route params)
files - Uploaded files (when file upload is enabled)
headers - Request headers (see Headers as Input Source)
Customizing for Specific Methods
import { createConfig } from "express-zod-api";
const config = createConfig({
inputSources: {
get: ["query", "params"],
// Allow DELETE to accept body data
delete: ["body", "params"],
// Include files for PATCH requests
patch: ["body", "params", "files"],
},
});
You can enable request headers as an input source, though this is an opt-in feature that requires careful consideration.
Configuration
import { createConfig } from "express-zod-api";
const config = createConfig({
inputSources: {
get: ["headers", "query", "params"], // headers have lowest priority
},
});
Give headers the lowest priority among other inputSources to avoid accidentally overwriting important data.
Best Practices
-
Use Middlewares for Headers: Consider handling headers in a
Middleware and declaring them in the security property to improve generated documentation.
-
Lowercase Headers: Request headers acquired this way are always lowercase when describing validation schemas.
Example with Middleware
import { Middleware } from "express-zod-api";
import { z } from "zod";
const headerAuthMiddleware = new Middleware({
security: { type: "header", name: "token" }, // documented in OpenAPI
input: z.object({
token: z.string(),
}),
handler: async ({ input: { token } }) => {
// Validate token
return { userId: "123" };
},
});
Example with Endpoint
import { defaultEndpointsFactory } from "express-zod-api";
import { z } from "zod";
const endpoint = defaultEndpointsFactory.build({
input: z.object({
"x-request-id": z.string(), // from request.headers (lowercase)
id: z.string(), // from request.query
}),
output: z.object({ success: z.boolean() }),
handler: async ({ input }) => {
// input["x-request-id"] and input.id are both available
return { success: true };
},
});
Priority Order Example
When multiple sources contain the same property name, the last one wins:
const config = createConfig({
inputSources: {
post: ["query", "body", "params"],
// If query, body, and params all have "id",
// the value from params will be used
},
});
Use Cases
const config = createConfig({
inputSources: {
get: ["headers", "query", "params"],
},
});
const endpoint = factory.build({
input: z.object({
"api-version": z.string().optional(),
id: z.string(),
}),
// ...
});
Flexible DELETE Operations
const config = createConfig({
inputSources: {
delete: ["body", "query", "params"],
},
});
// Now DELETE can accept data from body or query