Javascript validation library

javascript
validation

(Victor) #1

Data validation in js nu e treaba usoara, desi exista multe librarii care fac asta destul de bine.

La inceput am folosit si eu validator.js, ca mai tarziu sa vreau sa validez obiecte, asa ca am folosit Joi si mai apoi ajv.

Fiecare are plusuri si minusuri, asa ca am ajuns sa imi fac propria librarie pentru treaba asta - https://github.com/viczam/easevalidation.

Avantajul major e ca e foarte usor sa iti construiesti proprii validatori, in 99% din cazuri o functie care returneaza true sau false pe baza unui value argument e de ajuns.

Ai ca paradigme de validare atata una functionala - test(isNumber(), isMin(100), isFinite())(101), cat si chained validators: const isValid = number().isMin(100).isFinite().test(101)

Poti valida scalari, sau obiecte dupa o schema (asemanator cu Joi si ajv).

Vine cu toti validatorii din lodash (isObject, isNumber etc), toti validatorii din validator.js, toti cei date-related din date-fns (isMonday, isSameDay etc), plus o multime de alti custom validators pe care ii puteti vedea aici.

Exista si ceva teste, de ex aici puteti vedea cum validezi un obiect dupa o schema, sau aici, sau aici.

Partea buna e ca nu poti face doar data validation cat si data conversion. De ex, daca un number validator primeste un string care e un numar valid, poate face si type casting. La fel si daca vrei sa faci un mongo ObjectID validator care sa transforme string-ul intr-un ObjectID daca e valid.

Desi e la inceput, libraria e destul de completa - v1.0 se apropie.

Orice feedback e binevenit, iar daca vedeti imbunatatiri sau aveti idei de validatori noi, astept feedback prin github issues.


(Eugen) #2

Eu am incercat sa creez un API compozabil si cam asta este rezultatul, inca n-am o forma stabila sa-l public, food for thought.

Am plecat de la nevoia de a adauga custom validators on the fly si o metoda simpla sa adaug mesaje de eroare unde am nevoie.

import { isString, isNumber, isEmail, isEmpty } from "./assert"
import { and, not, or, min, max } from "./conditions"
import { validate, message } from "./schema"

// define custom conditions
const notEmpty = not(isEmpty)

const required = message(
  and(isString, notEmpty),
  'Field is required'
)

const password = and(
  isString,
  message(min(6), 'Minimum 6 characters are required'),
  message(max(32), 'Maximum 32 characters are allowed')
)

const email = and(required, message(isEmail, 'Invalid email'))
const minAge = message(min(18), 'You must be at least 18 years old')

// add conditions to schema
const schema = {
	firstName: required,
	lastName: required,
	email: email,
	password: password,
	age: minAge,
}

const errors = validate({ email: "asd23432" })

if (errors) {
  // errors <- an array with error messages
}
else {
  // all good
}

(Victor) #3

Este cam ce face si easevalidation.

import { test, validators } from 'easevalidation';

const {
  isSchema,
  isRequired,
  isString,
  isEmail,
  isLength,
  isDefault,
  isNumber,
  isMin,
} = validators;

const validate = test(isSchema({
  firstName: [isRequired(), isString()],
  lastName: [isRequired(), isString()],
  email: [isRequired(), isString(), isEmail()],
  password: [isRequired(), isString(), isLength({ min: 6, max: 32 })],
  age: [isDefault(18), isNumber(), isMin(18)],
}));

const isValid = validate({
  firstName: 'John',
  lastName: 'Doe',
  email: 'john@gmail.com',
  password: 'test1234',
  // notice it doesn't have an `age` property
});

validate.error => null
validate.value === {
  firstName: 'John',
  lastName: 'Doe',
  email: 'john@gmail.com',
  password: 'test1234',
  age: 18, // the default value
}

Pe de alta parte, asa cum am mentionat si in issue, poti folosi validate (from easevalidation) care va arunca o eroarea.
Eroarea returnata are tot ce ai nevoie - codul erorii, un obiect (key, value) cu field-urile si erorile asociate (if any) si multe altele.
Consider ca nu este responsabilitatea librariei sa returneze custom error messages - codul validatorului (de ex isLength), configurarea asociata (de ex: { min: 6, max: 32 }) si valoarea pe care o primeste e de ajuns sa afisezi ceva de genul: Parola ta - "test1234" - trebuie sa aiba minim 6 caractere si maxim 32! (stiu, nu e cel mai bun exemplu de error message :slight_smile: ).

Urmeaza sa scriu documentatie despre cum se poate face asta - am folosit easevalidation impreuna cu final-form ca sa pot validata anumite fields si sa afisez custom error messages, de ex.


(Ionuț Staicu) #4
import { createValidator, validate } from '..';

Treaba asta este ES6 sau este vreo transformare babel (sau specifică node)?


(Eugen) #5

ES6 Modules

# Default exports and named exports
import theDefault, { named1, named2 } from 'src/mylib';
import theDefault from 'src/mylib';
import { named1, named2 } from 'src/mylib';

# Renaming: import named1 as myNamed1
import { named1 as myNamed1, named2 } from 'src/mylib';

# Importing the module as an object
# (with one property per named export)
import * as mylib from 'src/mylib';

# Only load the module, donā€™t import anything
import 'src/mylib';

(Victor) #6

@navaru revenind la subiect, am adaugat aseara si isNot (plecand de la ideea ta) :slight_smile:

const isValid = test(isNot(isEmail()))('john@gmail.com'); // false

Referitor la error messages, odata ce ai un validation error (obtinut prin test sau validate), daca faci error.toJSON() o sa ai un output cu tot ce e nevoie pt a prezenta mesaje de erori cat mai detaliate (cum spuneam mai sus).

Exemplu:

import { validate, validators as v } from 'easevalidation';

try {
  validate(v.isEvery(v.isString(), v.isLength({ min: 3, max: 5 })))('a');
} catch (error) {
  console.log(JSON.stringify(error, null, 2));
}

O sa iti dea un output de forma:

{
  "code": "isEvery",
  "value": "a",
  "config": [
    {
      "code": "isString",
      "config": []
    },
    {
      "code": "isLength",
      "config": [
        {
          "min": 3,
          "max": 5
        }
      ]
    }
  ],
  "error": {
    "code": "isLength",
    "value": "a",
    "config": [
      {
        "min": 3,
        "max": 5
      }
    ]
  }
}

Eroarea are:

  • code - isEvery
  • config - configuarea pasata, in cazul asta e vorba un array de alti validatori
  • value - valoarea primita
  • error - nested error daca e cazul (si aici e cazul)
    • code - isLength
    • ā€¦

De aici iti poti da seama ca validarea a picat pentru ca isEvery a picat din cauza lui isLength.

Aici poti vedea o eroarea formata venita din isSchema cu o versiune short aici.