Datalize
Parameter, query, form data validation and filtering for Koa and Express.
See also this post about it on the Toptal Engineering Blog.
Installation
npm install --save datalize
Usage
Koa
const Koa = require('koa');
const Router = require('koa-router');
const datalize = require('datalize');
const field = datalize.field;
const app = new Koa();
// add any body parser
app.use(require('koa-body')());
const router = new Router();
router.post('/', datalize([
field('email', 'E-mail').required().email(),
field('firstname', 'Firstname').required(),
field('lastname', 'Lastname').required().trim(),
field('isTerms').bool(true),
]), (ctx, next) => {
ctx.body = {
status: 'success',
data: ctx.form,
};
});
app
.use(router.routes())
.use(router.allowedMethods());
Express
const express = require('express');
const datalize = require('datalize');
const field = datalize.field;
const app = express();
// add any body parser
app.use(require('body-parser').json());
app.post('/', datalize([
field('email', 'E-mail').required().email(),
field('firstname', 'Firstname').required(),
field('lastname', 'Lastname').required().trim(),
field('isTerms').bool(true),
]), (req, res) => {
res.send({
status: 'success',
data: req.form,
});
});
Methods
datalize(fields, options)
Creates Data object and returns validation middleware, which uses body as source. Result is set to context/request.form object.
datalize.params(fields, options)
Same as datalize(), but uses params as source. Result is set to context/request.data object.
datalize.query(fields, options)
Same as datalize(), but uses query as source. Result is set to context/request.data object.
datalize.field(name, label)
Returns: Field
Creates Field object.
datalize.set(name, value)
Sets global option for datalize.
datalize.set('autoValidate', true);
Options
type
Type: String, Default: 'form'
breakOnRequired
Type: Boolean, Default: true
If required error is returned, no other errors will be collected.
autoValidate
Type: Boolean, Default: false
Auto validates form and throws Data.Error if there is any error.
autoConvertToArray
Type: Boolean, Default: false
Auto converts field.array() fields to array.
error
Type: Error, Default: DataError
Error object thrown on autoValidate.
Filters
All filters and chainable.
field('rooms').required().int().range(1, 20)
condition: can throw errorfilter: updates value
field.required()
condition
field.requiredIf(nameOrFn, requiredValue)
condition- nameOrFn:
String|function - requiredValue:
any(optional) used only if nameOrFn is string
field.optional()
filter- The field is removed if value is undefined.
field.optionalIf(nameOrFn, requiredValue)
filter- nameOrFn:
String|function - requiredValue:
any(optional) used only if nameOrFn is string - The field is removed if value is undefined and conditions are passed.
field.patch()
filter- The same as optional but only if request's method is PATCH.
field.array()
filter- Returned value will be an Array.
field.container(children)
filter- children:
Array - Creates container will children fields, can be combined with
field.array().
field.split(separator = ',')
filter- Converts value to array via splitting by separator.
field.custom(fn)
condition,filter
field.bool(requiredValue)
condition,filter- requiredValue:
Boolean(optional) - Converts to boolean and if
requiredValueis provided, the field's value must be equal to it.
field.toggle(requiredValue)
condition,filter- requiredValue:
Number(optional) - Converts to number (0, 1) and if
requiredValueis provided, the field's value must be equal to it.
field.equal(compare)
condition- compare:
any
field.default(defaultValue)
filter- defaultValue:
any
field.nullable()
filter- Converts to null if is empty.
field.email()
condition
field.number()
condition,filter
field.uppercase()
filter
field.lowercase()
filter
field.trim()
filter
field.truncate(length)
filter- length:
Number
field.range(min, max)
condition,filter- min:
Number|String|Function(can be a number or name of a field or function that returns number) - max:
Number|String|Function(can be a number or name of a field or function that returns number)
field.min(min)
condition,filter- min:
Number|String|Function(can be a number or name of a field or function that returns number)
field.max(max)
condition,filter- max:
Number|String|Function(can be a number or name of a field or function that returns number)
field.length(min, max)
condition- min:
Number - max:
Number
field.minLength(min)
condition- min:
Number
field.maxLength(max)
condition- max:
Number
field.int()
condition,filter
field.float()
condition,filter
field.id()
condition,filter
field.select(options)
condition- options:
Array
File filters
field.file()
filter- Gets file from
req.filesobject.
field.mime(types)
condition- types:
Array
field.size(limit)
condition- limit:
Numberin Bytes
Custom filter
field.custom(function(value, result, ctx) {
if (['optionA', 'optionB'].indexOf(value) === -1) {
throw new Error('%s has invalid value.');
}
});
field.custom(function(value, result, ctx) {
return moment(value).format('YYYY/MM/DD');
});
field.custom(async (value, result, ctx) => {
const typeValue = await result.getValue('type');
if (type === 'business') {
return null;
}
});
Using custom filter globally
const datalize = require('datalize');
const field = datalize.field;
datalize.Field.prototype.isSlug = function(chars = 'a-z-') {
const regexp = new RegExp(`^([${chars}]+)Datalize
Parameter, query, form data validation and filtering for Koa and Express.
See also this post about it on the Toptal Engineering Blog.
Installation
npm install --save datalize
Usage
Koa
const Koa = require('koa');
const Router = require('koa-router');
const datalize = require('datalize');
const field = datalize.field;
const app = new Koa();
// add any body parser
app.use(require('koa-body')());
const router = new Router();
router.post('/', datalize([
field('email', 'E-mail').required().email(),
field('firstname', 'Firstname').required(),
field('lastname', 'Lastname').required().trim(),
field('isTerms').bool(true),
]), (ctx, next) => {
ctx.body = {
status: 'success',
data: ctx.form,
};
});
app
.use(router.routes())
.use(router.allowedMethods());
Express
const express = require('express');
const datalize = require('datalize');
const field = datalize.field;
const app = express();
// add any body parser
app.use(require('body-parser').json());
app.post('/', datalize([
field('email', 'E-mail').required().email(),
field('firstname', 'Firstname').required(),
field('lastname', 'Lastname').required().trim(),
field('isTerms').bool(true),
]), (req, res) => {
res.send({
status: 'success',
data: req.form,
});
});
Methods
datalize(fields, options)
Creates Data object and returns validation middleware, which uses __INLINE_CODE_0__ as source. Result is set to __INLINE_CODE_1__ object.
datalize.params(fields, options)
Same as __INLINE_CODE_2__, but uses __INLINE_CODE_3__ as source. Result is set to __INLINE_CODE_4__ object.
datalize.query(fields, options)
Same as __INLINE_CODE_5__, but uses __INLINE_CODE_6__ as source. Result is set to __INLINE_CODE_7__ object.
datalize.field(name, label)
Returns: __INLINE_CODE_8__
Creates Field object.
datalize.set(name, value)
Sets global option for datalize.
datalize.set('autoValidate', true);
Options
type
Type: __INLINE_CODE_9__, Default: __INLINE_CODE_10__
breakOnRequired
Type: __INLINE_CODE_11__, Default: __INLINE_CODE_12__
If required error is returned, no other errors will be collected.
autoValidate
Type: __INLINE_CODE_13__, Default: __INLINE_CODE_14__
Auto validates form and throws __INLINE_CODE_15__ if there is any error.
autoConvertToArray
Type: __INLINE_CODE_16__, Default: __INLINE_CODE_17__
Auto converts __INLINE_CODE_18__ fields to array.
error
Type: __INLINE_CODE_19__, Default: __INLINE_CODE_20__
Error object thrown on autoValidate.
Filters
All filters and chainable.
field('rooms').required().int().range(1, 20)
- __INLINE_CODE_21__: can throw error
- __INLINE_CODE_22__: updates value
field.required()
- __INLINE_CODE_23__
field.requiredIf(nameOrFn, requiredValue)
- __INLINE_CODE_24__
- nameOrFn: __INLINE_CODE_25__
- requiredValue: __INLINE_CODE_26__ (optional) used only if nameOrFn is string
field.optional()
- __INLINE_CODE_27__
- The field is removed if value is undefined.
field.optionalIf(nameOrFn, requiredValue)
- __INLINE_CODE_28__
- nameOrFn: __INLINE_CODE_29__
- requiredValue: __INLINE_CODE_30__ (optional) used only if nameOrFn is string
- The field is removed if value is undefined and conditions are passed.
field.patch()
- __INLINE_CODE_31__
- The same as optional but only if request's method is PATCH.
field.array()
- __INLINE_CODE_32__
- Returned value will be an Array.
field.container(children)
- __INLINE_CODE_33__
- children: __INLINE_CODE_34__
- Creates container will children fields, can be combined with __INLINE_CODE_35__.
field.split(separator = ',')
- __INLINE_CODE_36__
- Converts value to array via splitting by separator.
field.custom(fn)
- __INLINE_CODE_37__, __INLINE_CODE_38__
field.bool(requiredValue)
- __INLINE_CODE_39__, __INLINE_CODE_40__
- requiredValue: __INLINE_CODE_41__ (optional)
- Converts to boolean and if __INLINE_CODE_42__ is provided, the field's value must be equal to it.
field.toggle(requiredValue)
- __INLINE_CODE_43__, __INLINE_CODE_44__
- requiredValue: __INLINE_CODE_45__ (optional)
- Converts to number (0, 1) and if __INLINE_CODE_46__ is provided, the field's value must be equal to it.
field.equal(compare)
- __INLINE_CODE_47__
- compare: __INLINE_CODE_48__
field.default(defaultValue)
- __INLINE_CODE_49__
- defaultValue: __INLINE_CODE_50__
field.nullable()
- __INLINE_CODE_51__
- Converts to null if is empty.
field.email()
- __INLINE_CODE_52__
field.number()
- __INLINE_CODE_53__, __INLINE_CODE_54__
field.uppercase()
- __INLINE_CODE_55__
field.lowercase()
- __INLINE_CODE_56__
field.trim()
- __INLINE_CODE_57__
field.truncate(length)
- __INLINE_CODE_58__
- length: __INLINE_CODE_59__
field.range(min, max)
- __INLINE_CODE_60__, __INLINE_CODE_61__
- min: __INLINE_CODE_62__ (can be a number or name of a field or function that returns number)
- max: __INLINE_CODE_63__ (can be a number or name of a field or function that returns number)
field.min(min)
- __INLINE_CODE_64__, __INLINE_CODE_65__
- min: __INLINE_CODE_66__ (can be a number or name of a field or function that returns number)
field.max(max)
- __INLINE_CODE_67__, __INLINE_CODE_68__
- max: __INLINE_CODE_69__ (can be a number or name of a field or function that returns number)
field.length(min, max)
- __INLINE_CODE_70__
- min: __INLINE_CODE_71__
- max: __INLINE_CODE_72__
field.minLength(min)
- __INLINE_CODE_73__
- min: __INLINE_CODE_74__
field.maxLength(max)
- __INLINE_CODE_75__
- max: __INLINE_CODE_76__
field.int()
- __INLINE_CODE_77__, __INLINE_CODE_78__
field.float()
- __INLINE_CODE_79__, __INLINE_CODE_80__
field.id()
- __INLINE_CODE_81__, __INLINE_CODE_82__
field.select(options)
- __INLINE_CODE_83__
- options: __INLINE_CODE_84__
File filters
field.file()
- __INLINE_CODE_85__
- Gets file from __INLINE_CODE_86__ object.
field.mime(types)
- __INLINE_CODE_87__
- types: __INLINE_CODE_88__
field.size(limit)
- __INLINE_CODE_89__
- limit: __INLINE_CODE_90__ in Bytes
Custom filter
field.custom(function(value, result, ctx) {
if (['optionA', 'optionB'].indexOf(value) === -1) {
throw new Error('%s has invalid value.');
}
});
field.custom(function(value, result, ctx) {
return moment(value).format('YYYY/MM/DD');
});
field.custom(async (value, result, ctx) => {
const typeValue = await result.getValue('type');
if (type === 'business') {
return null;
}
});
Using custom filter globally
);
// make sure to return this.add() or this object to allow chaining
return this.add(function(value, result, ctx) {
if (!regexp.test(String(value))) {
throw new Error('%s contains invalid characters.');
}
});
};
// then the filter can be used anywhere
datalize([
field('slug').required().isSlug()
]);
Error handling
router.post('/', datalize([
field('email', 'E-mail').required().email(),
]), (ctx, next) => {
if (!ctx.form.isValid) {
ctx.status = 400;
ctx.body = {
status: 'error',
errors: ctx.form.errors,
};
return;
}
ctx.body = {
status: 'success',
data: ctx.form,
};
});
Global error handling
Koa
datalize.set('autoValidate', true);
// add to very beginning of koa middlewares
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
if (err instanceof datalize.Error) {
ctx.status = 400;
ctx.body = Object.assign({
status: 'error'
}, err.toJSON());
}
}
});
Express
datalize.set('autoValidate', true);
// add to very end of express middlewares
app.use(function(err, req, res, next) {
if (err instanceof datalize.Error) {
res.status(400).send(Object.assign({
status: 'error'
}, err.toJSON()));
}
});
License
MIT
Copyright (c) 2018 Andrej Adamcik