felv
felv is a fast easy and light (synchronous and asynchronous) JavaScript validator.
- Take only 2 minutes to know if felv is made for you.
- Take only 5 minutes to try it.
- Take only 15 minutes to read this page and learn everything you need to know!

Installation
Run the following command to add the package to your dev dependencies:
$ npm install --save felv
Use
const felv = require('felv');
Example
const schema = {
a: {
required: true,
type: 'number'
},
b: {
type: Array,
items: {
type: ['string', 'number']
}
},
c: {
type: Object,
properties: {
i: {
default: true,
type: 'boolean'
},
j: {
default: true,
type: 'boolean'
}
}
},
d: {
type: Object,
items: {
type: 'string'
}
}
};
const validatedValue = felv.validate(
{
a: 1,
b: [2, 'foo', 4],
c: {
i: false
},
d: {
x: 'foo',
y: 'bar'
}
},
schema
);
validatedValue
is equal to:
{
a: 1,
b: [2, 'foo', 4],
c: {
i: false,
j: true
},
d: {
x: 'foo',
y: 'bar'
}
}
Validation
Simple
You can easily validate a value from a schema:
const validatedValue = felv.validate(value, schema, options);
From a compiled schema
For repetitive validations, you should compile a validator to optimize validation performances. This can also be useful to detect schema format errors before validation occurs.
const validator = felv.compile(schema, options);
const validatedValue = validator.validate(value);
Schema
A schema is used to describe the format of a value. Each attribute of a schema is called a validation processor. A schema can embed other schemas to describe embedded structures thanks to some validation processors.
Validation processors list
Type
You can check the type of the value of a key:
{
foo: {
type: 'number'
},
bar: {
type: Date
},
foobar: {
type: ['number', 'string']
}
}
Matching values:
{
foo: 2,
bar: new Date(),
foobar: 3
}
{
foo: 2,
bar: new Date(),
foobar: 'foo'
}
Default
You can specify a default value:
{
foo: {
default: 3,
type: 'number'
},
bar: {
default: 6
}
}
Matching value:
{
foo: 2
}
Validated value:
{
foo: 2,
bar: 6
}
Required
You can specify a required value:
{
foo: {
required: true,
type: 'number'
},
bar: {
type: 'number'
}
}
Matching value:
{
foo: 2
}
Items
You can specify embedded items of arrays and objects:
{
foo: {
type: Array,
items: {
type: 'number'
}
},
bar: {
type: Object,
items: {
type: 'number'
}
}
}
Matching value:
{
foo: [2, 3, 7],
bar: {a: 1, b: 5}
}
Properties
You can specify properties of objects:
{
foo: {
type: Object,
properties: {
a: {
required: true,
type: 'number'
},
b: {
default: 'bar',
type: 'string'
}
}
}
}
Matching value:
{
foo: {a: 4}
}
Validated value:
{
foo: {a: 4, b: 'bar'}
}
Format
You can give a function to format input values:
{
foo: {
format: value => (Array.isArray(value) ? value : [value]),
type: Array,
items: {
type: 'string'
}
}
}
Matching value:
{
foo: 'bar'
}
Validated value:
{
foo: ['bar']
}
Validate
You can give a function to make custom validations and format output values:
{
foo: {
type: 'number',
validate: (value, expected) => {
if (value < 1 || value > 9) {
expected('a number between 1 and 9');
}
return value;
}
},
bar: {
type: 'number',
validate: value => (value > 1 ? value * 10 : value)
}
}
Matching value:
{
foo: 4,
bar: 2
}
Validated value:
{
foo: 4,
bar: 20
}
Error
You can specify a custom error message that will be set on error occurrence:
const schema = {
foo: {
required: true,
type: 'number',
error: 'You must specify a number for "foo"'
}
};
try {
felv.validate({foo: 'bar'}, schema);
} catch(error) {
console.log(error.customMessage);
}
Validation ways
You can specify different validation ways givin an array of schemas:
{
foo: [
{
required: true,
type: 'number'
},
{
default: 'bar',
type: 'string'
}
]
}
Matching values:
{foo: 2}
{foo: 'foo'}
{}
Corresponding validated values:
{foo: 2}
{foo: 'foo'}
{foo: 'bar'}
Asynchronous validation
You can use asynchronous functions in validation processors accepting a function (like format
and validate
) or directly return a Promise
. In that case, a Promise
will be returned by the validate
method.
Example:
const asyncSchema = {
foo: {
validate: async (value) => {
const validatedValue = await asyncValidationFunction(value);
return validatedValue + 1;
}
},
bar: {
format: (value) => {
return new Promise((resolve, reject) => {
asyncFormattingFunction(formattedValue => resolve(formattedValue));
});
}
}
};
const validatedValue = await felv.validate({foo: 3, bar: 4}, asyncSchema);
Validation processors dependencies
For simplicity, validation processors are independent from each other.
Validation processors order
Validation processors are processed in a specific order:
- format
- default
- required
- type
- items
- properties
- validate
- error
Options
You can modulate validation with following options:
- async
- convert
- formatting
- full
- immutable
- list
- namespace
- required
- validation
Async
Whether or not to force a Promise
result even on synchronous return.
Default value:
{
async: false
}
Convert
Whether or not to automatically convert some value types.
Default value:
{
convert: true
}
string
=> boolean
:
"false"
=> false
"true"
=> true
"0"
=> false
"1"
=> true
number
=> boolean
:
string
=> number
:
boolean
=> number
:
Formatting
A validation object to be passed as second argument (options
) in format
validation processors:
{
foo: {
format: (value, options) => {
}
}
}
Default value:
{
formatting: {}
}
Full
Whether or not to process full validation event after a first error occurred.
Default value:
{
full: false
}
Immutable
Whether or not to use the original value for validated value.
This increase performances but must not be used for cases where the original value must be preserved.
Default value:
{
immutable: false
}
List
Whether or not to process schema on a list of items (array or object) instead of an object with defined properties.
Default value:
{
list: false
}
Namespace
A namespace to prefix paths in error messages.
Default value:
{
namespace: '$'
}
Required
Whether or not fields are required by default.
Default value:
{
required: false
}
Validation
A validation object to be passed as third argument (options
) in validate
validation processors:
{
foo: {
validate: (value, expected, options) => {
}
}
}
expected
function take 3 arguments:
{string} expectedType
: Expected type (or value description)
{Array.<*>} [expectedValues]
: Optional expected values
{string} [customMessage]
: Optional custom error message
Default value:
{
validation: {}
}
Error handling
Simple example
const validator = felv.compile({foo: {type: 'string'}});
try {
const validatedValue = validator.validate({foo: 3});
} catch (error) {
console.log(error.name);
console.log(error.message);
console.log(error.path);
console.log(error.attribute);
console.log(error.subject);
console.log(error.expectedType);
console.log(error.expectedValues);
console.log(error.gotValue);
console.log(error.gotValue);
console.log(error.customMessage);
}
Full validation example
You can process full validation to retrieve all validation errors of a value against a schema.
This can be useful to validate a user form and return all errors for instance:
const schema = {
firstname: {
required: true,
type: 'string',
error: 'Firstname must be filled'
},
lastname: {
required: true,
type: 'string'
},
email: {
required: true,
type: 'string',
validate: (value, expected) => {
if (!/@/.test(value)) {
expected('email', null, 'Email is incorrect');
}
return value;
},
error: 'Email must be filled'
},
password: {
required: true,
type: 'string',
validate: (value, expected) => {
if (value.length < 8) {
expected('a string of at least 8 character');
} else if (!/[A-Z]/.test(value)) {
expected('a string with at least one uppercase letter');
}
return value;
},
error: 'Password must at least contains 8 characters with at least one uppercase letter'
}
};
const options = {
namespace: 'form',
full: true
};
const validator = felv.compile(schema, options);
try {
const validatedValue = validator.validate({
firstname: req.body.firstname,
lastname: req.body.lastname,
email: req.body.email,
password: req.body.email
});
} catch (error) {
console.log(error.name);
assert.deepEqual(error.pathErrorMessages, {
'form.firstname': 'Firstname must be filled',
'form.lastname': '[form.lastname](required) Expected value to be a defined value, got `undefined` instead',
'form.email': 'Email is incorrect',
'form.password': 'Password must at least contains 8 characters with at least one uppercase letter'
});
assert.deepEqual(error.errorMessages, [
'Email is incorrect',
'Firstname must be filled',
'Password must at least contains 8 characters with at least one uppercase letter',
'[form.lastname](required) Expected value to be a defined value, got `undefined` instead'
]);
}
Testing
Many npm
scripts are available to help testing:
$ npm run {script}
check
: lint and check unit and integration tests
lint
: lint
test
: check unit tests
test-coverage
: check coverage of unit tests
test-debug
: debug unit tests
test-integration
: check integration tests
test-performance
: check performance (just for fun)
test-watch
: work in TDD!
Use npm run check
to check that everything is ok.
Contributing
If you want to contribute, just fork this repository and make a pull request!
Your development must respect these rules:
You must keep test coverage at 100%.
License
MIT