API Reference
Universal Validation API
Complete API reference for MoroJS universal validation system supporting Zod, Joi, Yup, class-validator, and custom validation functions.
Validation Configuration
Universal Validation APItypescript
1import { createApp, z, joi, yup, classValidator, customValidator } from '@morojs/moro';
2import Joi from 'joi';
3import * as yupLib from 'yup';
4
5const app = await createApp();
6
7// 1. Zod validation (default - works directly)
8app.post('/users/zod')
9 .body(z.object({
10 name: z.string().min(2).max(50),
11 email: z.string().email(),
12 age: z.number().min(18).optional()
13 }))
14 .handler((req, res) => {
15 const { name, email, age } = req.body;
16 return { success: true, data: { name, email, age }, library: 'zod' };
17 });
18
19// 2. Joi validation (via adapter)
20app.post('/users/joi')
21 .body(joi(Joi.object({
22 name: Joi.string().min(2).max(50).required(),
23 email: Joi.string().email().required(),
24 age: Joi.number().min(18).optional()
25 })))
26 .handler((req, res) => {
27 return { success: true, data: req.body, library: 'joi' };
28 });
29
30// 3. Yup validation (via adapter)
31app.post('/users/yup')
32 .body(yup(yupLib.object({
33 name: yupLib.string().min(2).max(50).required(),
34 email: yupLib.string().email().required(),
35 age: yupLib.number().min(18).optional()
36 })))
37 .handler((req, res) => {
38 return { success: true, data: req.body, library: 'yup' };
39 });
40
41// Query validation
42app.get('/users')
43 .query(z.object({
44 limit: z.coerce.number().min(1).max(100).default(10),
45 search: z.string().optional()
46 }))
47 .handler((req, res) => {
48 // req.query is validated and typed
49 const { limit, search } = req.query;
50 return { success: true, data: [], limit, search };
51 });
52
53// Using the validate() wrapper (alternative approach).
54// validate(config, handler) WRAPS a handler - it is not (req, res, next)
55// middleware, so the handler is its second argument, not a separate one.
56app.post('/users-alt', validate(
57 {
58 body: z.object({
59 name: z.string().min(2),
60 email: z.string().email()
61 })
62 },
63 (req, res) => {
64 return { success: true, data: req.body };
65 }
66));
67
68// Schema-first route definition
69app.route({
70 method: 'GET',
71 path: '/users-schema',
72 validation: {
73 query: z.object({
74 limit: z.coerce.number().default(10),
75 search: z.string().optional()
76 })
77 },
78 handler: (req, res) => {
79 return { success: true, query: req.query };
80 }
81});Validation Adapter Functions
Adapter Functions
joi(schema) - Wrap Joi schemas
yup(schema) - Wrap Yup schemas
classValidator(Class) - Use decorated classes
customValidator(fn) - Custom validation
combineSchemas(...) - Merge schemas
normalizeValidationError() - Normalize errors
Universal Interface
ValidationSchema - Common interface
parseAsync(data) - Async validation
ValidationError - Error type
InferSchemaType<T> - Type inference
SchemaToOpenAPI - OpenAPI generation
ZodToOpenAPI - Zod to OpenAPI
Validation Error Handling
Custom Error Handlerstypescript
1// onValidationError lives inside the validation config, alongside the
2// schemas it applies to - pass them in one .validate() call.
3app.post('/users')
4 .validate({
5 body: CreateUserSchema,
6
7 // (errors: ValidationErrorDetail[], context) => { status, body, headers? }
8 onValidationError: (errors, context) => ({
9 status: 422,
10 body: {
11 error: 'VALIDATION_FAILED',
12 message: 'The request data is invalid',
13 path: context.request.path,
14 errors: errors.map(err => ({
15 field: err.field,
16 value: err.value,
17 message: err.message,
18 code: err.code
19 }))
20 }
21 })
22 })
23 .handler((req) => createUser(req.body));Global Validation Configurationtypescript
1const app = await createApp({
2 validation: {
3 // Global error handler
4 onError: (errors, context) => ({
5 status: 400,
6 body: {
7 error: 'INVALID_REQUEST',
8 details: errors
9 }
10 }),
11
12 // Validation options
13 abortEarly: false, // Return all errors
14 stripUnknown: true, // Remove unknown fields
15 allowUnknown: false // Reject unknown fields
16 }
17});