Skip to content

Getting started

Installation

The library contains both ESM and CJS uncompressed modules. Depending on your project setup, you may need to configure a module bundler.

Terminal window
npm install isomorphic-validation

Usage

  1. Define predicate functions:

    export const areTwoEqual = (value1, value2) => value1 === value2;
    export const areAllowedChars = (value) => /^[A-Za-z0-9._\-]*$/.test(value);
    export const isLongerThan = (number) => (value) => value.length > number;
    export const isShorterThan = (number) => (value) => value.length < number;
    export const isNotTakenC = (login) => fetch(
    'is-login-not-taken',
    { method: 'post', body: new URLSearchParams(`login=${login}`) }
    ).then(resp => resp.json());
    // a proper module bundler setup is needed to exclude this import from the client side bundle
    export const isNotTakenS = async (login) => {
    const repository = (await import('your-repository.js')).default;
    return !(await repository.getUserIdBy({login}));
    };

    You can also use validators from another library like validator.js:

    import isEmail from 'validator/es/lib/isEmail';
  2. Create Validation objects:

    const emailV = Validation();
    const loginV = Validation();
    const passwordV = Validation();
    const pwdConfirmV = Validation();
  3. Add the predicate functions as common (shared between forms) constraints to the validations:

    loginV
    .constraint(
    areAllowedChars,
    {
    invalidMsg: 'Allowed characters are: letters, numbers, ".", "_", "-".',
    next: false,
    },
    )
    .constraint(
    isLongerThan(4),
    { invalidMsg: 'Minimal length is 5 characters.', next: false },
    )
    .constraint(
    isShorterThan(33),
    { invalidMsg: 'Maximum length is 32 characters.' },
    );
    emailV
    .constraint(
    isEmail,
    {
    invalidMsg: 'Must conform to the e-mail format.'
    }
    );
  4. Create validation profiles (bind the validations to form fields):

    const [signinForm, signinV] = Validation.profile(
    '[name=signinForm]',
    ['login', 'password'],
    [loginV, passwordV],
    );
    const [signupForm, signupV] = Validation.profile(
    '[name=signupForm]',
    ['login', 'email', 'password', 'pwdConfirm'],
    [loginV, emailV, passwordV, pwdConfirmV],
    );
  5. Add form-specific and environment-specific constraints to cloned validations:

    Validation.glue(signupV.password, signupV.pwdConfirm)
    .constraint(
    areTwoEqual,
    { invalidMsg: 'Password and password confirmation must be the same.' },
    );
    signupV.login
    .client.constraint(
    isNotTakenC,
    {
    startedMsg: '⏳ Checking login for existence...',
    invalidMsg: 'Login must not be already registered.',
    debounce: 3000,
    },
    )
    .server.constraint(
    isNotTakenS
    );
  6. Add grouping validations as event handlers and middleware functions:

    For the client side:

    import { signinForm, signinV, signupForm, signupV } from 'your-validations-file-name.js';
    signinForm.addEventListener('input', signinV);
    signupForm.addEventListener('input', signupV);

    For the server side:

    import express from 'express';
    import bodyParser from 'body-parser';
    import { signinV, signupV } from 'your-validations-file-name.js';
    import { signinRequestHandler, signupRequestHandler, loginRequestHandler } from 'your-file-name.js';
    const app = express();
    const urlencodeParser = bodyParser.urlencoded({extended: false});
    app.post('/signin', urlencodeParser, signinV, signinRequestHandler);
    app.post('/signup', urlencodeParser, signupV, signupRequestHandler);
    app.post('/is-login-not-taken', urlencodeParser, signupV.login, loginRequestHandler);
  7. Connect side effects to validators’ and validations’ validity states:

    import {
    enableSubmitBtn, disableSubmitBtn, logSubmitAttempt,
    preserveFirstInvalidMsg, clearInvalidMsg, showFirstInvalidMsg,
    showFieldValidity,
    } from 'your-side-effects-file.js';
    // side effects for grouping validations
    [signinV, signupV].forEach(
    (validation) => validation
    .client
    .valid(enableSubmitBtn)
    .invalid(disableSubmitBtn)
    .server
    .invalid(logSubmitAttempt)
    );
    // side effects for field validations (grouped/nested validations)
    [...signinV.validations, ...signupV.validations].forEach(
    (validation) => validation
    .client
    .valid(clearInvalidMsg)
    .invalid(showFirstInvalidMsg)
    .changed(showFieldValidity)
    );
    // side effects for validators
    [...signinV.constraints, ...signupV.constraints].forEach(
    ([,validator]) => validator
    .client
    .invalid(preserveFirstInvalidMsg)
    );
  8. Don’t forget about error handling:

    import { handleError } from 'your-error-handlers-file.js';
    signinV.error(handleError);
    signupV.error(handleError)

You can see in action the client side part of a similar example on the Validation.profile() method page.