Overview
The @apisr/zod package extends Zod with additional utilities for metadata-based field resolution, allowing you to specify where field values should come from in HTTP requests.
Extended Zod Instance
Import the extended Zod instance that includes the .from() method:
import { z } from "@apisr/zod";
// or
import { zod } from "@apisr/zod";
To access the original Zod without extensions:
import { _z } from "@apisr/zod";
.from() Method
The .from() method allows you to specify where a field’s value should be extracted from in an HTTP request.
Signature
.from(
source: "query" | "params" | "body" | "headers" | "handler.payload",
options?: { key?: string | string[] }
)
Parameters
The source location for the field value:
"query" - URL query parameters
"params" - URL route parameters
"body" - Request body
"headers" - HTTP headers
"handler.payload" - Previous handler’s payload
Custom key(s) to use when extracting the value. Supports:
- Single string:
{ key: "user_id" }
- Array of strings (with fallback):
{ key: ["id", "userId"] }
- Dot notation for nested values:
{ key: "customer.name" }
If not provided, uses the field name from the schema.
Examples
Basic Source Mapping
import { z } from "@apisr/zod";
const schema = z.object({
// Field name matches source key
userId: z.string().from("params"),
search: z.string().from("query"),
name: z.string().from("body"),
});
Custom Keys
const schema = z.object({
// Map to different key in source
userId: z.string().from("params", { key: "id" }),
token: z.string().from("headers", { key: "authorization" }),
});
Fallback Keys
const schema = z.object({
// Try "id" first, fall back to "userId", then "user_id"
userId: z.string().from("params", { key: ["id", "userId", "user_id"] }),
});
Nested Values
const schema = z.object({
// Extract from nested object using dot notation
userName: z.string().from("body", { key: "user.name" }),
customerEmail: z.string().from("body", { key: "customer.contact.email" }),
});
Extracts the .from() metadata from a Zod object schema.
Signature
function resolveZodSchemaMeta(
schema: z.ZodObject<any>
): SchemaFieldsMeta
Return Type
type SchemaFieldsMeta = Record<string, FieldMeta>
interface FieldMeta {
from: string;
key?: string | string[];
}
Example
import { resolveZodSchemaMeta, z } from "@apisr/zod";
const schema = z.object({
name: z.string().from("body"),
id: z.string().from("params"),
token: z.string().from("headers", { key: "authorization" }),
});
const meta = resolveZodSchemaMeta(schema);
// {
// name: { from: "body" },
// id: { from: "params" },
// token: { from: "headers", key: "authorization" }
// }
resolveZodSchemaFromSources
Resolves field values from multiple sources based on the schema’s .from() metadata.
Signature
function resolveZodSchemaFromSources(
schema: z.ZodObject<any>,
sources: Record<string, Record<string, unknown>>
): Record<string, unknown>
Parameters
The Zod object schema with .from() metadata
sources
Record<string, Record<string, unknown>>
required
A map where keys match the from values (e.g., "query", "params", "body") and values are the corresponding data objects
Example
import { resolveZodSchemaFromSources, z } from "@apisr/zod";
const schema = z.object({
name: z.string().from("body"),
id: z.string().from("params"),
search: z.string().from("query"),
auth: z.string().from("headers", { key: "authorization" }),
prevName: z.string().from("handler.payload", { key: "name" }),
});
const resolved = resolveZodSchemaFromSources(schema, {
body: { name: "John" },
params: { id: "42" },
query: { search: "test" },
headers: { authorization: "Bearer token123" },
"handler.payload": { name: "prev-value" },
});
// Result:
// {
// name: "John",
// id: "42",
// search: "test",
// auth: "Bearer token123",
// prevName: "prev-value"
// }
With Nested Values
const schema = z.object({
userName: z.string().from("body", { key: "user.name" }),
userEmail: z.string().from("body", { key: "user.contact.email" }),
});
const resolved = resolveZodSchemaFromSources(schema, {
body: {
user: {
name: "John Doe",
contact: {
email: "[email protected]",
},
},
},
});
// Result:
// {
// userName: "John Doe",
// userEmail: "[email protected]"
// }
extendZod
Manually extend a Zod instance with the .from() method. This is automatically applied to the exported z instance.
Signature
function extendZod(zod: typeof z): ExtendResult
Example
import { extendZod } from "@apisr/zod";
import z from "zod";
const extendedZod = extendZod(z);
const schema = extendedZod.object({
id: extendedZod.string().from("params"),
});
Type Definitions
FromKey
type FromKey = "query" | "params" | "body" | "headers" | "handler.payload";
FromOptions
interface FromOptions {
key: string | string[];
}
interface FieldMeta {
from: string;
key?: string | string[];
}
type SchemaFieldsMeta = Record<string, FieldMeta>;
Complete Example
Here’s a complete example showing how to use Zod utilities with a handler:
import { z } from "@apisr/zod";
import { handler } from "@apisr/core";
import { checkSchema } from "@apisr/schema";
// Define schema with source mappings
const getUserSchema = z.object({
userId: z.string().from("params", { key: "id" }),
includeProfile: z.boolean().optional().from("query", { key: "profile" }),
authorization: z.string().from("headers", { key: "authorization" }),
});
// Create handler
const getUser = handler(
async ({ payload }) => {
// payload is validated and typed
const user = await db.users.findById(payload.userId);
if (!user) {
throw new Error("User not found");
}
return {
id: user.id,
username: user.username,
...(payload.includeProfile && { profile: user.profile }),
};
},
{
payload: getUserSchema,
}
);