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.
npm install isomorphic-validation
pnpm install isomorphic-validation
yarn install isomorphic-validation
For browser only usage, you can pull the library using CDN like jsdelivr.com or unpkg.
import { Validation, Predicate } from 'https://unpkg.com/isomorphic-validation@latest/dist/esm/index.js';import { renderFirstError, applyBox, toEventHandler } from 'https://unpkg.com/isomorphic-validation@latest/dist/esm/index.ui.js';
Usage
-
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;// client side part of the predicate// makes a request to the serverexport const isNotTakenC = (login) => fetch('is-login-not-taken',{ method: 'post', body: new URLSearchParams(`login=${login}`) }).then(resp => resp.json());// server side part of the predicate// makes a request to the database// a proper module bundler setup is needed to exclude this import from the client side bundleexport 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';import isStrongPassword from 'validator/es/lib/isStrongPassword'; -
Create
Validation
objects:const emailV = Validation();const loginV = Validation();const passwordV = Validation();const pwdConfirmV = Validation(); -
Add the predicate functions as common (shared between forms) constraints to the validations:
loginV.constraint(areAllowedChars,{errorMsg: 'Allowed characters are: letters, numbers, ".", "_", "-".',next: false,},).constraint(isLongerThan(4),{ errorMsg: 'Minimal length is 5 characters.', next: false },).constraint(isShorterThan(33),{ errorMsg: 'Maximum length is 32 characters.' },);emailV.constraint(isEmail,{errorMsg: 'Must conform to the e-mail format.'});passwordV.constraint(isStrongPassword,{errorMsg: 'Min. 8 symbols, 1 capital letter, 1 number, 1 special symbol.'}); -
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],); -
Add form-specific and environment-specific constraints to cloned validations:
Validation.glue(signupV.password, signupV.pwdConfirm).constraint(areTwoEqual,{ errorMsg: 'Password and password confirmation must be the same.' },);signupV.login.client.constraint(isNotTakenC,{startedMsg: '⏳ Checking login for existence...',errorMsg: 'Login must not be already registered.',debounce: 3000,},).server.constraint(isNotTakenS); -
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); -
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));You can also employ a set of useful side effect functions provided by the library’s UI module:
import { applyOutline } from 'isomorphic-validation/ui';[...signinV.validations, ...signupV.validations].forEach((validation) => validation.client.validated(applyOutline())); -
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.
Also see “Execution environment separation”.