express-zod-api
Version:
A Typescript framework to help you get an API server up and running with I/O schema validation and custom middlewares in minutes.
798 lines (797 loc) • 31.8 kB
TypeScript
import {
A as AbstractLogger,
C as ensureHttpError,
D as ClientMethod,
E as IOSchema,
N as createApiResponse,
O as Method,
S as defaultResultHandler,
T as FinalInputSchema,
_ as ServerConfig,
a as Handler,
b as ResultHandler,
c as Middleware,
d as FlatObject,
f as Tag,
g as CommonConfig,
h as AppConfig,
i as Endpoint,
j as LoggerOverrides,
k as BuiltinLogger,
l as EmptyObject,
m as getMessageFromError,
n as ServeStatic,
o as AbstractMiddleware,
p as TagOverrides,
r as AbstractEndpoint,
s as ExpressMiddleware,
t as Routing,
u as EmptySchema,
v as createConfig,
w as Extension,
x as arrayResultHandler,
y as AbstractResultHandler,
} from "./routing-xQnwfH2D.js";
import { i as OpenAPIContext } from "./documentation-helpers-C3vkQCz2.js";
import { z } from "zod";
import express, { CookieOptions, Request, Response } from "express";
import http from "node:http";
import { RequestOptions, ResponseOptions } from "node-mocks-http";
import "express-fileupload";
import { AugmentedRequest, Options, RateLimitInfo, RateLimitRequestHandler } from "express-rate-limit";
/**
* @desc Directives shared by both request and response Cache-Control headers.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#cache_directives
*/
interface CommonDirectives {
/**
* @desc Response: the response remains fresh for N seconds after it was generated.
* @desc Request: the client will accept a stored response that was generated at most N seconds ago.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#max-age
*/
maxAge?: number;
/**
* @desc Forces revalidation with the server before reuse.
* @desc In a response this tells caches to revalidate; in a request it asks caches to revalidate.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#no-cache
*/
noCache?: boolean;
/**
* @desc Prevents storing the response in any cache. In a response this instructs caches not to store.
* @desc In a request it asks caches not to store the request or its response.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#no-store
*/
noStore?: boolean;
/**
* @desc Prevents intermediaries from transforming the response body (e.g. converting images).
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#no-transform
*/
noTransform?: boolean;
/**
* @desc Allows a stale cached response to be reused for N seconds when the origin server returns an error.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#stale-if-error
*/
staleIfError?: number;
}
/**
* @desc Directives that clients send in requests to express their caching preferences.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#request_directives
*/
interface CacheControl extends CommonDirectives {
/**
* @desc The client will accept a stored response that is stale for up to N seconds beyond its freshness lifetime.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#max-stale
*/
maxStale?: number;
/**
* @desc The client requires a stored response that will remain fresh for at least N more seconds.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#min-fresh
*/
minFresh?: number;
/**
* @desc The client wants a response only from the cache. Throw createHttpError(504) in this case.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#only-if-cached
*/
onlyIfCached?: boolean;
}
/**
* @desc Directives that servers send in responses to control how caches store and reuse the response.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#response_directives
*/
interface CachePolicy extends CommonDirectives {
/**
* @desc Restricts which caches may store the response.
* @example "public" — any cache (browser, proxy, CDN); for static assets, responses without user-specific data.
* @example "private" — browser only; for user-specific and personalized content.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#response_directives
*/
scope?: "public" | "private";
/**
* @desc Overrides max-age for shared caches (proxies, CDNs). Ignored by private (browser) caches.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#s-maxage
*/
sMaxAge?: number;
/**
* @desc Forces all caches to revalidate stale responses with the origin server before reusing them.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#must-revalidate
*/
mustRevalidate?: boolean;
/**
* @desc Forces proxies and CDNs to revalidate stale responses with the origin server before reusing them.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#proxy-revalidate
*/
proxyRevalidate?: boolean;
/**
* @desc A cache must understand the caching requirements for the response's status code before storing it.
* @desc Pair with no-store as a fallback for caches that don't support it.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#must-understand
*/
mustUnderstand?: boolean;
/**
* @desc Indicates that the response body will never change while fresh.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#immutable
*/
immutable?: boolean;
/**
* @desc Allows a stale response to be served in the background while the cache revalidates it, for up to N seconds.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#stale-while-revalidate
*/
staleWhileRevalidate?: number;
}
/**
* @desc Creates a Middleware providing caching helpers.
* @param defaultPolicy — Optional default Cache-Control policy applied to all responses.
* @example createCacheMiddleware({ noCache: true, scope: "private" })
*/
declare const createCacheMiddleware: (defaultPolicy?: CachePolicy) => Middleware<
FlatObject,
{
/**
* @desc Provides the parsed If-None-Match request header into an array of ETags. Can also be '*' wildcard.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-None-Match
*/
readonly ifNoneMatch: string[] | "*" | undefined;
/**
* @desc Provides the parsed If-Modified-Since request header having the timestamp of the client's cached copy.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-Modified-Since
*/
readonly ifModifiedSince: Date | undefined;
/**
* @desc Provides the parsed Cache-Control request header to reveal the client's caching intent.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control
*/
readonly cacheControl: CacheControl | undefined;
/**
* @desc Augments the Cache-Control response header, merging with the defaultPolicy if provided.
* @desc Pass `undefined` for a directive to unset the default value.
* @see defaultPolicy
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching
*/
addCachePolicy: (policy: CachePolicy) => void;
/**
* @desc Sets the ETag response header with a unique identifier for this version of the resource.
* @see ifNoneMatch
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag
*/
setETag: (value: string) => void;
/**
* @desc Sets the Last-Modified response header to the timestamp when the resource was last changed.
* @see ifModifiedSince
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Last-Modified
*/
setLastModified: (date: Date) => void;
/**
* @desc Sets the Vary response header to the list of request headers that influence the response.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Vary
*/
setVary: (...headers: string[]) => void;
/**
* @desc Sets the Expires response header with an explicit expiration date. Consider addCachePolicy({ maxAge }).
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Expires
*/
setExpires: (date: Date) => void;
/**
* @desc Sets the Clear-Site-Data response header with the "cache" directive to remove all cached responses.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Clear-Site-Data
*/
clearSiteData: () => void;
/**
* @desc Sends an HTTP 304 Not Modified empty response and ends the response stream.
* @example return ctx.notModified() as never; // to satisfy the handler's return type
* @see ifNoneMatch
* @see ifModifiedSince
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/304
*/
notModified: () => void;
},
string,
undefined
>;
/**
* @desc Creates a Middleware providing cookie-setting convenience methods.
* @param baseOptions — Default options applied to every setCookie / clearCookie call.
* @example createCookieMiddleware({ httpOnly: true, secure: true, path: "/" })
*/
declare const createCookieMiddleware: (baseOptions?: CookieOptions) => Middleware<
FlatObject,
{
/**
* @desc Reads a cookie value. Checks signedCookies first, then falls back to cookies.
* @requires cookie-parser
* @see ServerConfig.cookies
* */
getCookie: (name: string) => z.core.util.JSONType | undefined;
/** @desc Sets a cookie on the response. Express converts non-string values to JSON. */
setCookie: (name: string, value: string | z.core.util.JSONType, overrides?: CookieOptions) => void;
/** @desc Clears a cookie on the response. */
clearCookie: (name: string, overrides?: Omit<CookieOptions, "expires" | "maxAge">) => void;
},
string,
undefined
>;
/**
* @desc Creates an ExpressMiddleware that enforces rate limits using express-rate-limit.
* @requires express-rate-limit
* @param options — Partial options passed to the express-rate-limit constructor.
* @example createRateLimitMiddleware({ windowMs: 60000, max: 100 })
*/
declare const createRateLimitMiddleware: (options?: Partial<Options>) => ExpressMiddleware<
AugmentedRequest,
import("express-serve-static-core").Response<any, Record<string, any>, number>,
{
rateLimit: RateLimitInfo & Pick<RateLimitRequestHandler, "getKey" | "resetKey">;
}
>;
interface BuildProps<
IN extends IOSchema,
OUT extends IOSchema | z.ZodVoid,
MIN extends IOSchema | undefined,
CTX extends FlatObject,
SCO extends string,
> {
/**
* @desc Input schema of the Endpoint, combining properties from all the enabled input sources (path params, headers)
* @default z.object({})
* @see defaultInputSources
* */
input?: IN;
/** @desc The schema by which the returns of the Endpoint handler is validated */
output: OUT;
/** @desc The Endpoint handler receiving the validated inputs, returns of added Middlewares (ctx) and a logger */
handler: Handler<z.output<FinalInputSchema<MIN, IN>>, z.input<OUT>, CTX>;
/** @desc The operation description for the generated Documentation (may use Markdown) */
description?: string;
/** @desc The operation summary for the generated Documentation (short plain string) */
summary?: string;
/** @desc The operation ID for the generated Documentation (must be unique) */
operationId?: string | ((method: ClientMethod) => string);
/**
* @desc HTTP method(s) this endpoint can handle
* @default "get" unless method is explicitly defined in Routing keys
* */
method?: Method | [Method, ...Method[]];
/**
* @desc Scope(s) from the list of the ones defined by the added Middlewares having "oauth2" security type
* @see OAuth2Security
* */
scope?: SCO | SCO[];
/**
* @desc Tag(s) for generating Documentation. For establishing constraints:
* @see TagOverrides
* */
tag?: Tag | Tag[];
/** @desc Marks the operation deprecated in the generated Documentation */
deprecated?: boolean;
}
/**
* @desc Creates a factory for building Endpoints. It can be extended by adding Middlewares that enrich the context
* available to the Endpoint handler. It requires a ResultHandler to respond consistently.
* @see Middleware
* @see ResultHandler
* */
declare class EndpointsFactory<
IN extends IOSchema | undefined = undefined,
CTX extends FlatObject = EmptyObject,
SCO extends string = string,
> {
protected resultHandler: AbstractResultHandler;
protected schema: IN;
protected middlewares: AbstractMiddleware[];
/**
* @param resultHandler An instance of ResultHandler for handling both Endpoint outputs and all possible errors.
* @see ResultHandler
* */
constructor(resultHandler: AbstractResultHandler);
/**
* @desc Attaches a Middleware to the factory, extending the context available to Endpoints built on it.
* Accepts either a Middleware instance or a plain object compatible with the Middleware constructor.
* @see Middleware
* */
addMiddleware<RET extends FlatObject, ASCO extends string, AIN extends IOSchema | undefined = undefined>(
subject: Middleware<CTX, RET, ASCO, AIN> | ConstructorParameters<typeof Middleware<CTX, RET, ASCO, AIN>>[0],
): EndpointsFactory<Extension<IN, AIN>, (CTX extends Record<string, never> ? RET : CTX) & RET, SCO & ASCO>;
/** @desc Shorthand for .addMiddleware(createCookieMiddleware()) */
useCookies(...args: Parameters<typeof createCookieMiddleware>): EndpointsFactory<
Extension<IN, undefined>,
(CTX extends Record<string, never>
? {
getCookie: (name: string) => z.core.util.JSONType | undefined;
setCookie: (
name: string,
value: string | z.core.util.JSONType,
overrides?: import("express").CookieOptions,
) => void;
clearCookie: (name: string, overrides?: Omit<import("express").CookieOptions, "expires" | "maxAge">) => void;
}
: CTX) & {
getCookie: (name: string) => z.core.util.JSONType | undefined;
setCookie: (
name: string,
value: string | z.core.util.JSONType,
overrides?: import("express").CookieOptions,
) => void;
clearCookie: (name: string, overrides?: Omit<import("express").CookieOptions, "expires" | "maxAge">) => void;
},
SCO
>;
/** @desc Shorthand for .addMiddleware(createCacheMiddleware()) */
useCache(...args: Parameters<typeof createCacheMiddleware>): EndpointsFactory<
Extension<IN, undefined>,
(CTX extends Record<string, never>
? {
readonly ifNoneMatch: string[] | "*" | undefined;
readonly ifModifiedSince: Date | undefined;
readonly cacheControl: CacheControl | undefined;
addCachePolicy: (policy: CachePolicy) => void;
setETag: (value: string) => void;
setLastModified: (date: Date) => void;
setVary: (...headers: string[]) => void;
setExpires: (date: Date) => void;
clearSiteData: () => void;
notModified: () => void;
}
: CTX) & {
readonly ifNoneMatch: string[] | "*" | undefined;
readonly ifModifiedSince: Date | undefined;
readonly cacheControl: CacheControl | undefined;
addCachePolicy: (policy: CachePolicy) => void;
setETag: (value: string) => void;
setLastModified: (date: Date) => void;
setVary: (...headers: string[]) => void;
setExpires: (date: Date) => void;
clearSiteData: () => void;
notModified: () => void;
},
SCO
>;
/** @desc Shorthand for .addMiddleware(createRateLimitMiddleware()) */
useRateLimit(...args: Parameters<typeof createRateLimitMiddleware>): EndpointsFactory<
Extension<IN, undefined>,
(CTX extends Record<string, never>
? {
rateLimit: import("express-rate-limit").RateLimitInfo &
Pick<import("express-rate-limit").RateLimitRequestHandler, "getKey" | "resetKey">;
}
: CTX) & {
rateLimit: import("express-rate-limit").RateLimitInfo &
Pick<import("express-rate-limit").RateLimitRequestHandler, "getKey" | "resetKey">;
},
SCO
>;
/**
* @desc Shorthand for addExpressMiddleware(). Use it for wrapping native Express middlewares.
* @see addExpressMiddleware
* */
use: <R extends Request, S extends Response, AOUT extends FlatObject = Record<string, never>>(
nativeMw: (request: R, response: S, next: import("express").NextFunction) => any,
params_1?:
| {
provider?: ((request: R, response: S) => AOUT | Promise<AOUT>) | undefined;
transformer?: (err: Error) => Error;
}
| undefined,
) => EndpointsFactory<Extension<IN, undefined>, (CTX extends Record<string, never> ? AOUT : CTX) & AOUT, SCO>;
/**
* @desc Wraps a native Express middleware and attaches it to the factory as a Middleware. Optionally, a `provider`
* can extract context properties from the request and response, and a `transformer` can convert errors.
* @see ExpressMiddleware
* */
addExpressMiddleware<R extends Request, S extends Response, AOUT extends FlatObject = EmptyObject>(
...params: ConstructorParameters<typeof ExpressMiddleware<R, S, AOUT>>
): EndpointsFactory<Extension<IN, undefined>, (CTX extends Record<string, never> ? AOUT : CTX) & AOUT, SCO>;
/**
* @desc Extends the context available to Endpoints built on this factory by resolving additional properties
* from an asynchronous callback. The callback receives the current accumulated context, allowing further
* context values to depend on previously provided ones. This is a shorthand for addMiddleware() with no schema.
* @see addMiddleware
* */
addContext<RET extends FlatObject>(
provider: (current: CTX) => Promise<RET>,
): EndpointsFactory<Extension<IN, undefined>, (CTX extends Record<string, never> ? RET : CTX) & RET, SCO>;
/**
* @desc Builds an Endpoint using the accumulated Middlewares, the ResultHandler, and the given configuration.
* The output is validated against the output schema; the handler receives the validated input and context.
* @see Endpoint
* */
build<BOUT extends IOSchema, BIN extends IOSchema = EmptySchema>({
input,
output: outputSchema,
operationId,
scope,
tag,
method,
...rest
}: BuildProps<BIN, BOUT, IN, CTX, SCO>): Endpoint<FinalInputSchema<IN, BIN>, BOUT, CTX>;
/**
* @desc shorthand for build() having output schema assigned with an empty object
* @see build
* */
buildVoid<BIN extends IOSchema = EmptySchema>({
handler,
...rest
}: Omit<BuildProps<BIN, z.ZodVoid, IN, CTX, SCO>, "output">): Endpoint<
FinalInputSchema<IN, BIN>,
z.ZodObject<{}, z.core.$strip>,
CTX
>;
}
/**
* @desc The factory based on the default ResultHandler: suitable for JSON responses.
* @see defaultResultHandler
* */
declare const defaultEndpointsFactory: EndpointsFactory<undefined, Record<string, never>, string>;
/**
* @deprecated Resist the urge of using it: this factory is designed only to simplify the migration of legacy APIs.
* @desc Responding with an array is a bad practice keeping your endpoints from evolving without breaking changes.
* @desc The result handler of this factory expects your endpoint to have the property 'items' in the output schema
*/
declare const arrayEndpointsFactory: EndpointsFactory<undefined, Record<string, never>, string>;
declare const attachRouting: (
config: AppConfig,
routing: Routing,
) => {
notFoundHandler: express.RequestHandler<
import("express-serve-static-core").ParamsDictionary,
any,
any,
import("qs").ParsedQs,
Record<string, any>
>;
logger: AbstractLogger | BuiltinLogger;
};
declare const createServer: (
config: ServerConfig,
routing: Routing,
) => {
app: import("express-serve-static-core").Express;
logger: AbstractLogger | BuiltinLogger;
servers: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>[];
};
/** @desc An error related to the wrong Routing declaration */
declare class RoutingError extends Error {
name: string;
readonly cause: {
method: Method;
path: string;
};
constructor(message: string, method: Method, path: string);
}
/**
* @desc An error related to the generating of the documentation
* */
declare class DocumentationError extends Error {
name: string;
readonly cause: string;
constructor(message: string, { method, path, isResponse }: Pick<OpenAPIContext, "path" | "method" | "isResponse">);
}
/** @desc An error related to the input and output schemas declaration */
declare class IOSchemaError extends Error {
name: string;
}
/** @desc An error of validating the Endpoint handler's returns against the Endpoint output schema */
declare class OutputValidationError extends IOSchemaError {
readonly cause: z.ZodError;
name: string;
constructor(cause: z.ZodError);
}
/** @desc An error of validating the input sources against the Middleware or Endpoint input schema */
declare class InputValidationError extends IOSchemaError {
readonly cause: z.ZodError;
name: string;
constructor(cause: z.ZodError);
}
declare class MissingPeerError extends Error {
name: string;
constructor(module: string);
}
interface TestingProps<REQ, LOG> {
/**
* @desc Additional properties to set on Request mock
* @default { method: "GET", headers: { "content-type": "application/json" } }
* */
requestProps?: REQ;
/**
* @link https://www.npmjs.com/package/node-mocks-http
* @default { req: requestMock }
* */
responseOptions?: ResponseOptions;
/**
* @desc Additional properties to set on config mock
* @default { cors: false, logger }
* */
configProps?: Partial<CommonConfig>;
/**
* @desc Additional properties to set on logger mock
* @default { info, warn, error, debug }
* */
loggerProps?: LOG;
}
declare const testEndpoint: <LOG extends FlatObject, REQ extends RequestOptions>({
endpoint,
...rest
}: TestingProps<REQ, LOG> & {
/** @desc The endpoint to test */
endpoint: AbstractEndpoint;
}) => Promise<{
requestMock: import("node-mocks-http").MockRequest<
Request<
import("express-serve-static-core").ParamsDictionary,
any,
any,
import("qs").ParsedQs,
Record<string, any>
> &
REQ
>;
responseMock: import("node-mocks-http").MockResponse<Response<any, Record<string, any>>>;
loggerMock: AbstractLogger &
LOG & {
_getLogs: () => Record<"debug" | "info" | "warn" | "error", unknown[]>;
};
}>;
interface MiddlewareLike<RET extends FlatObject> {
execute(...params: Parameters<AbstractMiddleware["execute"]>): Promise<RET>;
}
declare const testMiddleware: <LOG extends FlatObject, REQ extends RequestOptions, RET extends FlatObject>({
middleware,
ctx,
...rest
}: TestingProps<REQ, LOG> & {
/** @desc The middleware to test */
middleware: MiddlewareLike<RET>;
/** @desc The aggregated returns of previously executed middlewares */
ctx?: FlatObject;
}) => Promise<{
output: Partial<RET>;
requestMock: import("node-mocks-http").MockRequest<
Request<
import("express-serve-static-core").ParamsDictionary,
any,
any,
import("qs").ParsedQs,
Record<string, any>
> &
REQ
>;
responseMock: import("node-mocks-http").MockResponse<Response<any, Record<string, any>>>;
loggerMock: AbstractLogger &
LOG & {
_getLogs: () => Record<"debug" | "info" | "warn" | "error", unknown[]>;
};
}>;
type EventsMap = Record<string, z.ZodType>;
interface Emitter<E extends EventsMap> extends FlatObject {
/** @desc Returns true when the connection was closed or terminated */
isClosed: () => boolean;
/** @desc Abort signal bound to the client connection lifecycle */
signal: AbortSignal;
/** @desc Sends an event to the stream according to the declared schema */
emit: <K extends keyof E>(event: K, data: z.input<E[K]>) => void;
}
declare class EventStreamFactory<E extends EventsMap> extends EndpointsFactory<undefined, Emitter<E>> {
constructor(events: E);
}
interface DateInParams extends z.core.GlobalMeta {
examples?: string[];
}
interface DateOutParams extends z.core.GlobalMeta {
examples?: string[];
}
declare const DEFAULT_ITEMS_NAME: "items";
/** @desc Common pagination config: shared by offset and cursor styles. */
interface CommonPaginationConfig<T extends z.ZodType = z.ZodType, K extends string = typeof DEFAULT_ITEMS_NAME> {
/** @desc Zod schema for each item in the paginated list. */
itemSchema: T;
/**
* @desc The name of the property containing the list of items.
* @default "items"
* */
itemsName?: K;
/**
* @desc Maximum allowed page size (client request is capped to this).
* @default 100
*/
maxLimit?: number;
/**
* @desc Default page size when the client omits the limit parameter.
* @default 20
*/
defaultLimit?: number;
}
/**
* @desc Configuration for offset-based pagination (limit and offset).
* @example { style: "offset", itemSchema, maxLimit: 50, defaultLimit: 10 }
*/
interface OffsetPaginatedConfig<
T extends z.ZodType = z.ZodType,
K extends string = typeof DEFAULT_ITEMS_NAME,
> extends CommonPaginationConfig<T, K> {
/** @desc Discriminator for offset-style pagination. */
style: "offset";
}
/**
* @desc Configuration for cursor-based pagination (cursor and limit).
* @example { style: "cursor", itemSchema, maxLimit: 50, defaultLimit: 10 }
*/
interface CursorPaginatedConfig<
T extends z.ZodType = z.ZodType,
K extends string = typeof DEFAULT_ITEMS_NAME,
> extends CommonPaginationConfig<T, K> {
/** @desc Discriminator for cursor-style pagination. */
style: "cursor";
}
/** @desc Request params for offset pagination. */
type OffsetInput = z.ZodObject<{
/** @desc Page size (number of items per page). */
limit: z.ZodDefault<z.ZodCoercedNumber>;
/** @desc Number of items to skip from the start of the list. */
offset: z.ZodDefault<z.ZodCoercedNumber>;
}>;
/** @desc Request params for cursor pagination. */
type CursorInput = z.ZodObject<{
/** @desc Opaque cursor for the next page; omit for the first page. */
cursor: z.ZodOptional<z.ZodString>;
/** @desc Page size (number of items per page). */
limit: z.ZodDefault<z.ZodCoercedNumber>;
}>;
/** @desc Response shape for offset pagination. */
type OffsetOutput<T extends z.ZodType, K extends string> = z.ZodObject<
{ [ITEMS in K]: z.ZodArray<T> } & {
/** @desc Total number of items across all pages. */
total: z.ZodNumber;
/** @desc Page size used for this response. */
limit: z.ZodNumber;
/** @desc Offset used for this response. */
offset: z.ZodNumber;
}
>;
/** @desc Response shape for cursor pagination. */
type CursorOutput<T extends z.ZodType, K extends string> = z.ZodObject<
{ [ITEMS in K]: z.ZodArray<T> } & {
/** @desc Cursor for the next page, or null if there are no more pages. */
nextCursor: z.ZodNullable<z.ZodString>;
/** @desc Page size used for this response. */
limit: z.ZodNumber;
}
>;
/** @desc Return type of ez.paginated() for offset style. */
interface OffsetPaginatedResult<T extends z.ZodType = z.ZodType, K extends string = typeof DEFAULT_ITEMS_NAME> {
/** @desc Zod schema for offset pagination request params. */
input: OffsetInput;
/** @desc Zod schema for offset pagination response. */
output: OffsetOutput<T, K>;
}
/** @desc Return type of ez.paginated() for cursor style. */
interface CursorPaginatedResult<T extends z.ZodType = z.ZodType, K extends string = typeof DEFAULT_ITEMS_NAME> {
/** @desc Zod schema for cursor pagination request params. */
input: CursorInput;
/** @desc Zod schema for cursor pagination response. */
output: CursorOutput<T, K>;
}
/**
* @desc Creates a pagination helper with a single config for both request params and response shape.
* Use the returned `.input` as the endpoint input schema and `.output` as the response schema.
* Compose with other params via `.input.and(z.object({ ... }))`.
*
* @param config - Pagination config; `style` discriminates offset vs cursor; `itemSchema` defines each list item.
* @returns Object with `input` (Zod schema for pagination params) and `output` (Zod schema for paginated response).
*
* @example
* const pagination = ez.paginated({ style: "offset", maxLimit: 100, defaultLimit: 20, itemSchema: userSchema });
* endpoint.input = pagination.input.and(z.object({ ... }));
* endpoint.output = pagination.output;
*/
declare function paginated<T extends z.ZodType, K extends string = typeof DEFAULT_ITEMS_NAME>(
config: OffsetPaginatedConfig<T, K>,
): OffsetPaginatedResult<T, K>;
declare function paginated<T extends z.ZodType, K extends string = typeof DEFAULT_ITEMS_NAME>(
config: CursorPaginatedConfig<T, K>,
): CursorPaginatedResult<T, K>;
declare const base: z.ZodObject<
{
raw: z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>;
},
z.core.$strip
>;
type Base = typeof base;
declare const extended: <S extends z.core.$ZodShape>(
extra: S,
) => z.ZodObject<
(
"raw" & keyof S extends never
? {
raw: z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>;
} & { -readonly [P in keyof S]: S[P] }
: ({
raw: z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>;
} extends infer T_1 extends z.core.util.SomeObject
? { [K in keyof T_1 as K extends keyof S ? never : K]: T_1[K] }
: never) & { [K_1 in keyof { -readonly [P in keyof S]: S[P] }]: { -readonly [P in keyof S]: S[P] }[K_1] }
) extends infer T
? { [k in keyof T]: T[k] }
: never,
z.core.$strip
>;
declare function raw(): Base;
declare function raw<S extends z.core.$ZodShape>(extra: S): ReturnType<typeof extended<S>>;
declare const ez: {
dateIn: ({
examples,
...rest
}?: DateInParams) => import("zod").ZodPipe<
import("zod").ZodPipe<
import("zod").ZodUnion<readonly [import("zod").ZodISODate, import("zod").ZodISODateTime]>,
import("zod").ZodTransform<Date, string>
>,
import("zod").ZodDate
>;
dateOut: (
meta?: DateOutParams,
) => import("zod").ZodPipe<
import("zod").ZodPipe<import("zod").ZodDate, import("zod").ZodTransform<string, Date>>,
import("zod").ZodISODateTime
>;
form: <S extends import("zod/v4/core").$ZodShape>(
base: S | import("zod").ZodObject<S>,
) => import("zod").ZodObject<S, import("zod/v4/core").$strip>;
upload: () => import("zod").ZodCustom<
import("express-fileupload").UploadedFile,
import("express-fileupload").UploadedFile
>;
raw: typeof raw;
buffer: () => import("zod").ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>;
paginated: typeof paginated;
};
export {
BuiltinLogger,
DocumentationError,
EndpointsFactory,
EventStreamFactory,
InputValidationError,
type LoggerOverrides,
type Method,
Middleware,
MissingPeerError,
OutputValidationError,
ResultHandler,
type Routing,
RoutingError,
ServeStatic,
type TagOverrides,
arrayEndpointsFactory,
arrayResultHandler,
attachRouting,
createApiResponse,
createCacheMiddleware,
createConfig,
createCookieMiddleware,
createRateLimitMiddleware,
createServer,
defaultEndpointsFactory,
defaultResultHandler,
ensureHttpError,
ez,
getMessageFromError,
testEndpoint,
testMiddleware,
};