![require(esm) Backported to Node.js 20, Paving the Way for ESM-Only Packages](https://cdn.sanity.io/images/cgdhsj6q/production/be8ab80c8efa5907bc341c6fefe9aa20d239d890-1600x1097.png?w=400&fit=max&auto=format)
Security News
require(esm) Backported to Node.js 20, Paving the Way for ESM-Only Packages
require(esm) backported to Node.js 20, easing the transition to ESM-only packages and reducing complexity for developers as Node 18 nears end-of-life.
express-zod-api
Advanced tools
A Typescript library to help you get an API server up and running with I/O schema validation and custom middlewares in minutes.
Start your API server with I/O schema validation and custom middlewares in minutes.
The API always operates object schemas for input and output.
Starting with version 0.7.0, union and intersection of object schemas are also supported (.or()
, .and()
).
The object being validated is the request.query
for GET request, the request.body
for PUT, PATCH and POST requests, or their merging for DELETE requests.
Middlewares can handle validated inputs and the original request
, for example, to perform the authentication or provide the endpoint's handler with some request properties like the actual method.
The returns of middlewares are combined into the options
parameter available to the next middlewares and the endpoint's handler.
The handler's parameter input
combines the validated inputs of all connected middlewares along with the handler's one.
The result that the handler returns goes to the ResultHandler
which is responsible for transmission of the final response or possible error.
All inputs and outputs are validated and there are also advanced powerful features like transformations and refinements. The diagram below can give you a better idea of the dataflow.
yarn add express-zod-api
# or
npm install express-zod-api
Add the following options to your tsconfig.json
file in order to make it work as expected:
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true
}
}
See full example here.
import {ConfigType} from 'express-zod-api';
const config: ConfigType = {
server: {
listen: 8090,
},
cors: true,
logger: {
level: 'debug',
color: true
}
};
See all available options here.
import {EndpointsFactory} from 'express-zod-api';
const endpointsFactory = new EndpointsFactory();
You can also instantly add middlewares to it using .addMiddleware()
method.
import {z} from 'express-zod-api';
const setUserEndpoint = endpointsFactory.build({
methods: ['post'],
input: z.object({
id: z.number(),
name: z.string,
}),
output: z.object({
timestamp: z.number(),
}),
handler: async ({input: {id, name}, options, logger}) => {
logger.debug(`Requested id: ${id}`);
logger.debug('Options:', options); // provided by middlewares
return { timestamp: Date.now() };
}
});
You can add middlewares to the endpoint by using .addMiddleware()
before .build()
.
import {Routing} from 'express-zod-api';
const routing: Routing = {
v1: {
setUser: setUserEndpoint
}
};
This implementation sets up setUserEndpoint
to handle requests to the /v1/setUser
path.
import {createServer} from 'express-zod-api';
createServer(config, routing);
You can create middlewares separately using createMiddleware()
function and connect them later.
All returns of the connected middlewares are combined into the options
argument of the endpoint's handler.
The inputs of middlewares are combined with the inputs of the endpoint's handler.
import {
createMiddleware, z, Method, createHttpError
} from 'express-zod-api';
// This one provides the method of the request to your
// endpoint. It's useful for the ones that handle
// multiple types of request (GET, POST, ...)
const methodProviderMiddleware = createMiddleware({
input: z.object({}).nonstrict(),
middleware: async ({request}) => ({
method: request.method.toLowerCase() as Method,
})
});
// This one performs the authentication using a key from
// the input and a token from headers. It supplies the
// endpoint with a user from a database.
const authMiddleware = createMiddleware({
input: z.object({
key: z.string().nonempty()
}),
middleware: async ({input: {key}, request, logger}) => {
logger.debug('Checking the key and token...');
const user = await db.Users.findOne({key});
if (!user) {
throw createHttpError(401, 'Invalid key');
}
if (request.headers['token'] !== user.token) {
throw createHttpError(401, 'Invalid token');
}
return { user };
}
});
You can implement additional validation inside the schema:
import {createMiddleware, z} from 'express-zod-api';
const authMiddleware = createMiddleware({
input: z.object({
key: z.string().nonempty()
.refine((key) => key === '123', 'Invalid key')
}),
...
})
Since parameters of GET requests come in the form of strings, there is often a need to transform them into numbers or arrays of numbers.
import {z} from 'express-zod-api';
const getUserEndpoint = endpointsFactory.build({
methods: ['get'],
input: z.object({
id: z.string().transform((id) => parseInt(id, 10)),
ids: z.string().transform(
(ids) => ids.split(',').map((id) => parseInt(id, 10))
)
}),
output: z.object({...}),
handler: async ({input: {id, ids}, logger}) => {
logger.debug('id', id); // type: number
logger.debug('ids', ids); // type: number[]
}
});
ResultHandler
is the type of function that is responsible for transmission of the final response or possible error.
The defaultResultHandler
sets the HTTP status code and ensures the following type of the response:
type ApiResponse<T> = {
status: 'success',
data: T
} | {
status: 'error',
error: {
message: string;
}
};
You have two options to customize the ResultHandler
: globally or at the endpoint level:
import {ConfigType, ResultHandler} from 'express-zod-api';
const resultHandler: ResultHandler =
({error, input, output, request, response, logger}) => {};
const config: ConfigType = { resultHandler, ... };
// or
import {EndpointsFactory} from 'express-zod-api';
const endpointsFactory = new EndpointsFactory().setResultHandler(
({error, input, output, request, response, logger}) => {}
);
You can specify your custom Winston logger in config:
import * as winston from 'winston';
import {ConfigType, createServer} from 'express-zod-api';
const config: ConfigType = {
logger: winston.createLogger(),
...
};
createServer(config, routing);
You can instantiate your own express app and connect your endpoints the following way.
import * as express from 'express';
import {ConfigType, attachRouting} from 'express-zod-api';
const app = express();
const config: ConfigType = {app, ...};
const routing = {...};
attachRouting(config, routing);
app.listen();
Please note that in this case you probably need to: parse request.body
, call app.listen()
and handle 404
errors yourself;
You can export only the types of your endpoints for your front-end:
export type MyEndpointType = typeof endpoint;
Then use provided helpers to obtain their input and output types:
import {EndpointInput, EndpointOutput} from 'express-zod-api';
import {MyEndpointType} from '../your/backend';
type MyEndpointInput = EndpointInput<MyEndpointType>;
type MyEndpointOutput = EndpointOutput<MyEndpointType>;
You can generate the specification of your API the following way and write it to a .yaml
file:
import {OpenAPI} from 'express-zod-api';
const yamlString = new OpenAPI({
routing,
version: '1.2.3',
title: 'Example API',
serverUrl: 'http://example.com'
}).builder.getSpecAsYaml();
Unfortunately Typescript does not perform excess property check for objects resolved in Promise
, so there is no error during development of endpoint's output.
import {z} from 'express-zod-api';
endpointsFactory.build({
methods, input,
output: z.object({
anything: z.number()
}),
handler: async () => ({
anything: 123,
excessive: 'something' // no type error
})
});
You can achieve this check by assigning the output schema to a constant and reusing it in additional definition of handler's return type:
import {z} from 'express-zod-api';
const output = z.object({
anything: z.number()
});
endpointsFactory.build({
methods, input, output,
handler: async (): Promise<z.input<typeof output>> => ({
anything: 123,
excessive: 'something' // error TS2322, ok!
})
});
v1.0.0
FAQs
A Typescript framework to help you get an API server up and running with I/O schema validation and custom middlewares in minutes.
The npm package express-zod-api receives a total of 4,150 weekly downloads. As such, express-zod-api popularity was classified as popular.
We found that express-zod-api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
require(esm) backported to Node.js 20, easing the transition to ESM-only packages and reducing complexity for developers as Node 18 nears end-of-life.
Security News
PyPI now supports iOS and Android wheels, making it easier for Python developers to distribute mobile packages.
Security News
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.