@sliit-foss/zelebrate
v1.1.1
Published
Express middleware which wraps around the zod validation library to provide a simple way to validate request bodies, query parameters, and headers.
Downloads
96
Maintainers
Readme
@sliit-foss/zelebrate
Express middleware which wraps around the Zod validation library to provide a simple way to validate request bodies, query parameters, and headers. Heavy inspiration from celebrate which is a middleware for the Joi validation library.
Zelebrate exposes the same API as celebrate, but uses Zod for validation instead of Joi. This means that you can use Zelebrate in the same way you would use celebrate, but with the added benefits of Zod's type inference and validation capabilities.
Further this library exposes a utility function named zelebrateStack which preserves the request segment types within the express handlers at lower levels automatically. Use this to build rock solid typesafe express applications.
Installation
# using npm
npm install @sliit-foss/zelebrate
# using yarn
yarn add @sliit-foss/zelebrateUsage
const express = require("express");
const bodyParser = require("body-parser");
const { zelebrate, z, errors, Segments } = require("@sliit-foss/zelebrate");
const app = express();
app.use(bodyParser.json());
app.post(
"/signup",
zelebrate({
[Segments.BODY]: z.object({
name: z.string(),
age: z.number().int(),
role: z.string().default("admin")
}),
[Segments.QUERY]: z.object({
token: z.string().uuid()
})
}),
(req, res) => {
// At this point, req.body has been validated and
// req.body.role is equal to req.body.role if provided in the POST or set to 'admin' by zod
}
);
app.use(errors());Type Safe Express Handlers
const express = require("express");
const bodyParser = require("body-parser");
const { zelebrateStack, z, errors, Segments } = require("@sliit-foss/zelebrate");
const app = express();
app.use(bodyParser.json());
app.post(
"/signup",
zelebrateStack({
[Segments.BODY]: z.object({
name: z.string(),
age: z.number().int(),
role: z.string().default("admin")
}),
[Segments.QUERY]: z.object({
token: z.string().uuid()
})
})(
/** Add any number of handlers. req.body and req.query will be typed within all of them */
(req, res) => {
// req.body and req.query are type safe.
}
)
);
app.use(errors());API
zelebrate does not have a default export. The following methods encompass the public API.
zelebrate(schema, [opts])
Returns a function with the middleware signature ((req, res, next)).
requestRules- anobjectwherekeycan be one of the values fromSegmentsand thevalueis a zod validation schema. Only the keys specified will be validated against the incoming request object. If you omit a key, that part of thereqobject will not be validated. A schema must contain at least one valid key.[opts]- an optionalobjectwith the following keys. Defaults to{}.mode- optionalModesfor controlling the validation mode zelebrate uses. Defaults topartial.
zelebrator([opts], schema)
This is a curried version of zelebrate It is curried with lodash.curryRight so it can be called in all the various fashions that API supports. Returns a function with the middleware signature ((req, res, next)).
[opts]- an optionalobjectwith the following keys. Defaults to{}.mode- optionalModesfor controlling the validation mode zelebrate uses. Defaults topartial.
requestRules- anobjectwherekeycan be one of the values fromSegmentsand thevalueis a zod validation schema. Only the keys specified will be validated against the incoming request object. If you omit a key, that part of thereqobject will not be validated. A schema must contain at least one valid key.
This is an example use of curried zelebrate in a real server.
const express = require("express");
const { zelebrator, z, errors, Segments } = require("@sliit-foss/zelebrate");
const app = express();
// now every instance of `zelebrate` will use these same options so you only
// need to do it once.
const zelebrate = zelebrator({ mode: Modes.FULL });
// validate all incoming request headers for the token header
// if missing or not the correct format, respond with an error
app.use(
zelebrate({
[Segments.HEADERS]: z
.object({
token: z.string().regex(/abc\d{3}/)
})
.catchall(z.unknown())
})
);
app.get(
"/",
zelebrate({
[Segments.HEADERS]: z
.object({
name: z.string()
})
.catchall(z.unknown())
}),
(req, res) => {
res.send("hello world");
}
);
app.use(errors());errors([opts])
Returns a function with the error handler signature ((err, req, res, next)). This should be placed with any other error handling middleware to catch zelebrate errors. If the incoming err object is an error originating from zelebrate, errors() will respond a pre-build error object. Otherwise, it will call next(err) and will pass the error along and will need to be processed by another error handler.
[opts]- an optionalobjectwith the following keysstatusCode-numberthat will be used for the response status code in the event of an error. Must be greater than 399 and less than 600. It must also be a number available to the node HTTP module. Defaults to 400.message-stringthat will be used for themessagevalue sent out by the error handler. Defaults to'Validation failed'
If the error response format does not suite your needs, you are encouraged to write your own and check isZelebrateError(err) to format zelebrate errors to your liking.
Zelebrate augments the error objects returned by zod and adds a pretty method to it. This method will return a human readable string of the first error in the validation chain.
Errors origintating from the zelebrate() middleware are ZelebrateError objects.
z
zelebrate exports the version of zod it is using internally. For maximum compatibility, you should use this version when creating schemas used with zelebrate.
Segments
An enum containing all the segments of req objects that zelebrate can validate against.
{
BODY: 'body',
COOKIES: 'cookies',
HEADERS: 'headers',
PARAMS: 'params',
QUERY: 'query',
SIGNEDCOOKIES: 'signedCookies',
}Modes
An enum containing all the available validation modes that zelebrate can support.
PARTIAL- ends validation on the first failure.FULL- validates the entire request object and collects all the validation failures in the result.
new ZelebrateError([message], [status])
Creates a new ZelebrateError object. Extends the built in Error object.
message- optionalstringmessage. Defaults to'Validation failed'.status- optionalnumberstatus code. Defaults to422.
ZelebrateError has the following public properties:
details- aMapof all validation failures. Thekeyis aSegmentsand the value is a zod validation error.
isZelebrateError(err)
Returns true if the provided err object originated from the zelebrate middleware, and false otherwise. Useful if you want to write your own error handler for zelebrate errors.
err- an error object
Additional Details
Validation Order
zelebrate validates request values in the following order:
req.headersreq.paramsreq.queryreq.cookies(assumingcookie-parseris being used)req.signedCookies(assumingcookie-parseris being used)req.body(assumingbody-parseris being used)
Mutation Warning
If you use any of zods's transformation APIs (transform, coerce, etc.) zelebrate will override the source value with the changes applied by the transformation
For example, if you validate req.query and have a default value in your zod schema, if the incoming req.query is missing a value for default, during validation zelebrate will overwrite the original req.query with the transformed result.
Additional Info
According the the HTTP spec, GET requests should not include a body in the request payload. For that reason, zelebrate does not validate the body on GET requests.
Issues
Before opening issues on this repo, make sure your zod schema is correct and working as you intended. The bulk of this code is just exposing the zod API as express middleware. All of the heavy lifting still happens inside zod.
