Skip to main content

Zodios Application

A Zodios application is a simple adapter for Express. It's an express instance but with full typesafety and autocompletion.

info

For more information on how to use express, check out the Express documentation

zodiosApp

To upgrade an existing express application with typesafety, replace your express() calls to zodiosApp(api)

function zodiosApp(api?: ZodiosEndpointDescriptions, options?: ZodiosAppOptions): ZodiosApp

ctx.app

You can also create a context aware express application with ctx.app:

Context.app(api?: ZodiosEndpointDescriptions, options?: ZodiosAppOptions): ZodiosApp

Options

PropertyTypeDescription
expressExpressoptional express instance - default to express()
enableJsonBodyParserbooleanenable json body parser - default to true
validatebooleanenable zod input validation - default to true
transformbooleanenable zod input transformation - default to false
validationErrorHandlerRouterValidationErrorHandlererror handler for validation errors - default to defaulErrorHandler
type RouterValidationErrorHandler = (
err: {
context: string;
error: z.ZodIssue[];
},
req: Request,
res: Response,
next: NextFunction
): void;

Examples

Express Application from context

import { zodiosContext } from "@zodios/express";
import z from "zod";
import { userApi } from "../../common/api";
import { userMiddleware } from "./userMiddleware";

const ctx = zodiosContext(z.object({
user: z.object({
id: z.number(),
name: z.string(),
isAdmin: z.boolean(),
}),
}));

const app = ctx.app(userApi);
// middleware that adds the user to the context
app.use(userMiddleware);

// auto-complete path fully typed and validated input params (body, query, path, header)
// ▼ ▼ ▼
app.get("/users/:id", (req, res) => {
// auto-complete user fully typed
// ▼
if(req.user.isAdmin) {
// res.json is typed thanks to zod
return res.json({
// auto-complete req.params.id
// ▼
id: req.params.id,
name: "John Doe",
});
}
return res.status(403).end();
})

app.listen(3000);

Express Application

/src/server/app.ts
import { zodiosApp } from "@zodios/express";
import { userApi } from "../../common/api";

// just an express adapter that is aware of your api, app is just an express app with type annotations and validation middlewares
const app = zodiosApp(userApi);

// auto-complete path fully typed and validated input params (body, query, path, header)
// ▼ ▼ ▼
app.get("/users/:id", (req, res) => {
// res.json is typed thanks to zod
res.json({
// auto-complete req.params.id
// ▼
id: req.params.id,
name: "John Doe",
});
})

app.listen(3000);

Error Handling

Zodios express can infer the status code to match your API error response and also have your errors correctly typed.

/src/server/app.ts
import { makeApi } from "@zodios/core";
import { zodiosApp } from "@zodios/express";
import { z } from "zod";

const userApi = makeApi([
{
method: "get",
path: "/users/:id",
alias: "getUser",
description: "Get a user",
response: z.object({
id: z.number(),
name: z.string(),
}),
errors: [
{
status: 404,
response: z.object({
code: z.string(),
message: z.string(),
id: z.number(),
}),
}, {
status: 'default', // default status code will be used if error is not 404
response: z.object({
code: z.string(),
message: z.string(),
}),
},
],
},
]);

const app = zodiosApp(userApi);
app.get("/users/:id", (req, res) => {
try {
const id = +req.params.id;
const user = service.findUser(id);
if(!user) {
// match error 404 schema with auto-completion
res.status(404).json({
code: "USER_NOT_FOUND",
message: "User not found",
id, // compile time error if you forget to add id
});
} else {
// match response schema with auto-completion
res.json(user);
}
} catch(err) {
// match default error schema with auto-completion
res.status(500).json({
code: "INTERNAL_ERROR",
message: "Internal error",
});
}
})

app.listen(3000);