JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 9
  • Score
    100M100P100Q42242F
  • License MIT

A world-class Joi validation error formatter for structured, user-friendly error handling.

Package Exports

  • joi-schema-validator
  • joi-schema-validator/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (joi-schema-validator) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

joi-schema-validator

npm license build downloads

joi-schema-validator is a structured and user-friendly error handler for Joi validation in Node.js applications. It simplifies error messages and supports synchronous, asynchronous, and middleware-based validation.


Key Features

  • Synchronous Validation (validate): Validate input and return structured error messages.
  • Asynchronous Validation (validateAsync): Support for database lookups or external API validation.
  • Express Middleware (validateMiddleware): Automatically validate incoming requests.
  • Custom Error Messages: Define global or field-specific messages.
  • Enhanced Type Safety: Strongly typed validation results.
  • Supports Nested Objects: Handles deeply nested field errors.
  • Enterprise-Ready: Lightweight and high-performance.

Installation

Install via npm or yarn:

npm install joi-schema-validator

or

yarn add joi-schema-validator

Usage

Import the required functions:

import {
    validate,
    validateAsync,
    validateMiddleware
} from 'joi-schema-validator';

1. Synchronous Validation (validate)

Example:

import Joi from 'joi';
import { validate } from 'joi-schema-validator';

const schema = Joi.object({
    name: Joi.string().min(3).required().label('Name'),
    age: Joi.number().min(18).required().label('Age'),
    email: Joi.string().email().required().label('Email'),
    address: Joi.string().optional().label('Address')
});

const customMessages = {
    'string.min': '{#label} must have at least {#limit} characters.',
    'name.string.min':
        'Name should be at least {#limit} characters long. Please provide a valid name.'
};

const result = validate(
    schema,
    { name: 'Jo', age: 16, email: 'invalid', address: '' },
    customMessages
);

console.log(result);

Output Response

{
    "value": {
        "name": "Jo",
        "age": 16,
        "email": "invalid",
        "address": ""
    },
    "error": [
        {
            "field": "name",
            "type": "string.min",
            "message": "Name should be at least 3 characters long. Please provide a valid name."
        },
        {
            "field": "age",
            "type": "number.min",
            "message": "Age must be greater than or equal to 18."
        },
        {
            "field": "email",
            "type": "string.email",
            "message": "Email must be a valid email address."
        }
    ]
}

2. Asynchronous Validation (validateAsync)

Example:

import Joi from 'joi';
import { validateAsync } from 'joi-schema-validator';

const schema = Joi.object({
    username: Joi.string()
        .min(3)
        .required()
        .external(async (value) => {
            const existingUsers = ['existingUser', 'user123'];
            if (existingUsers.includes(value)) {
                throw new Error('Username already exists.');
            }
            return value;
        }),
    email: Joi.string().email().required()
});

async function runAsyncValidation() {
    const result = await validateAsync(schema, {
        username: 'existingUser',
        email: 'test@example.com'
    });
    console.log(result);
}

runAsyncValidation();

Output Response

{
    "value": {
        "username": "ex",
        "email": "test@example.com"
    },
    "error": [
        {
            "field": "username",
            "type": "external",
            "message": "Username already exists."
        }
    ]
}

3. Express Middleware (validateMiddleware)

Example:

import express from 'express';
import Joi from 'joi';
import { validateMiddleware } from 'joi-schema-validator';

const app = express();
app.use(express.json());

const userSchema = Joi.object({
    name: Joi.string().min(3).required().label('Name'),
    email: Joi.string().email().required().label('Email')
});

app.post('/user', validateMiddleware(userSchema), (req, res) => {
    res.json({ success: true, data: req.body });
});

app.listen(3000, () => console.log('Server running on port 3000'));

Example Request

POST /user
Content-Type: application/json

{
  "name": "Jo",
  "email": "invalidEmail"
}

Output Response (HTTP 400 Bad Request)

{
    "errors": [
        {
            "field": "name",
            "type": "string.min",
            "message": "Name must have at least 3 characters."
        },
        {
            "field": "email",
            "type": "string.email",
            "message": "Email must be a valid email address."
        }
    ]
}

4. Custom Error Messages

joi-schema-validator allows you to provide your own error message templates that override (or extend) the default messages. You can specify:

  1. Global Overrides:
    These custom messages apply to every validation error of a given type. For example, setting a custom message for 'string.min' will change the message for any string that fails the minimum length validation.

  2. Field-Level Overrides:
    These are specific to a particular field. For instance, you can provide a custom message for the 'name' field when it fails a minimum length check using the key 'name.string.min'.

  3. Fallback Behavior:
    If no custom message is provided for a particular error, the default message (from defaultErrorMessages) is used. If neither is available, the error message defaults to "<field> is invalid.".

How to Define Custom Error Messages

Custom messages are passed as an ErrorMessages object. The keys in this object are either:

  • Global error type keys, e.g.:
    {
      'string.min': '{#label} must have at least {#limit} characters.'
    }
  • Field-specific keys, e.g.:
    {
      'name.string.min': 'Name should be at least {#limit} characters long. Please provide a valid name.'
    }

Placeholders like {#label} and {#limit} will be replaced with values from the Joi validation context (e.g., the field label or the limit specified in the schema).

Usage in Different APIs

1. Synchronous Validation

When using the validate function, pass the custom messages as the third parameter:

import Joi from 'joi';
import { validate } from 'joi-schema-validator';

const schema = Joi.object({
    name: Joi.string().min(3).required().label('Name'),
    age: Joi.number().min(18).required().label('Age'),
    email: Joi.string().email().required().label('Email')
});

// Define custom error messages
const customMessages = {
    // Global override for all string.min errors
    'string.min': '{#label} must have at least {#limit} characters.',
    // Field-specific override for the "name" field
    'name.string.min':
        'Name should be at least {#limit} characters long. Please provide a valid name.'
};

const result = validate(
    schema,
    { name: 'Jo', age: 16, email: 'invalid' },
    customMessages
);
if (result.error) {
    console.log('Formatted Errors:', result.formattedErrors);
} else {
    console.log('Validated Data:', result.value);
}

Output Response:

  • For name failing the min check, you’ll see the custom field-specific message.
  • For other errors (like age and email), you’ll see the global custom message or default messages if no override exists.
{
    "value": {
        "name": "Jo",
        "age": 16,
        "email": "invalid",
        "address": ""
    },
    "error": [
        {
            "field": "name",
            "type": "string.min",
            "message": "Name should be at least 3 characters long. Please provide a valid name."
        },
        {
            "field": "age",
            "type": "number.min",
            "message": "Age must be greater than or equal to 18."
        },
        {
            "field": "email",
            "type": "string.email",
            "message": "Email must be a valid email address."
        }
    ]
}

2. Asynchronous Validation

The usage is similar when using validateAsync:

import Joi from 'joi';
import { validateAsync } from 'joi-schema-validator';

const schema = Joi.object({
    username: Joi.string()
        .min(3)
        .required()
        .external(async (value) => {
            const existingUsers = ['existingUser'];
            if (existingUsers.includes(value)) {
                throw new Error('Username already exists.');
            }
            return value;
        }),
    email: Joi.string().email().required()
});

const customMessages = {
    'string.min': '{#label} must have at least {#limit} characters.',
    'username.string.min':
        'Username should be at least {#limit} characters long. Please choose a longer username.'
};

async function runAsyncValidation() {
    const result = await validateAsync(
        schema,
        { username: 'ex', email: 'test@example.com' },
        customMessages
    );
    if (result.error) {
        console.log('Async Formatted Errors:', result.formattedErrors);
    } else {
        console.log('Async Validated Data:', result.value);
    }
}

runAsyncValidation();

Output Response:

  • If the username fails the min check, the field-specific message is used.
  • Other errors use the global message or fallback to the default.
{
    "value": {
        "username": "ex",
        "email": "test@example.com"
    },
    "error": [
        {
            "field": "username",
            "type": "external",
            "message": "Username already exists."
        }
    ]
}

3. Express Middleware for Validation

When using the Express middleware, simply pass the custom messages to the middleware function:

import express from 'express';
import Joi from 'joi';
import { validateMiddleware } from 'joi-schema-validator';

const app = express();
app.use(express.json());

const userSchema = Joi.object({
    name: Joi.string().min(3).required().label('Name'),
    email: Joi.string().email().required().label('Email')
});

const customMessages = {
    'string.min': '{#label} must have at least {#limit} characters.',
    'name.string.min': 'Name should be at least {#limit} characters long.'
};

app.post(
    '/user',
    validateMiddleware(userSchema, customMessages),
    (req, res) => {
        res.json({ success: true, data: req.body });
    }
);

app.listen(3000, () => console.log('Server running on port 3000'));

Output Response if Validation Fails:
If a POST request to /user contains invalid data, the middleware will respond with aN e JSOrror payload like:

{
    "success": false,
    "errors": [
        {
            "field": "name",
            "type": "string.min",
            "message": "Name should be at least 3 characters long."
        },
        {
            "field": "email",
            "type": "string.email",
            "message": "Email must be a valid email address."
        }
    ]
}

API Reference

1. validate(schema, input, messages?, options?)

  • Validates input synchronously against a Joi schema.
  • Returns a ValidationResult<T>.
  • Example:
    const result = validate(schema, input, customMessages, {
        abortEarly: false
    });

2. validateAsync(schema, input, messages?, options?)

  • Validates input asynchronously, useful for database or external API checks.
  • Returns a Promise<ValidationResult<T>>.
  • Example:
    const result = await validateAsync(schema, input);

3. validateMiddleware(schema, messages?)

  • Express middleware that validates req.body.
  • If validation fails, returns a 400 response.
  • Example:
    app.post('/route', validateMiddleware(schema), handler);

Type Definitions

export interface ValidationErrorItemFormatted {
    field: string;
    type: string;
    message: string;
}

export interface ErrorMessages {
    [key: string]: string;
}

export interface ValidationResult<T> {
    value: T;
    error: ValidationError | null;
    formattedErrors?: ValidationErrorItemFormatted[];
}

License

This project is licensed under the MIT License.

Issues & Support

For issues, report them here.


Author

Developed by Pavan Dulam.