Parameter, query, form data validation and filtering for Koa and Express.
See also this post about it on the Toptal Engineering Blog.
npm install --save datalize
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());
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,
});
});
Creates Data object and returns validation middleware, which uses body
as source. Result is set to context/request.form
object.
Same as datalize()
, but uses params
as source. Result is set to context/request.data
object.
Same as datalize()
, but uses query
as source. Result is set to context/request.data
object.
Returns: Field
Creates Field object.
Sets global option for datalize.
datalize.set('autoValidate', true);
Type: String
, Default: 'form'
Type: Boolean
, Default: true
If required error is returned, no other errors will be collected.
Type: Boolean
, Default: false
Auto validates form and throws Data.Error
if there is any error.
Type: Boolean
, Default: false
Auto converts field.array()
fields to array.
Type: Error
, Default: DataError
Error object thrown on autoValidate.
All filters and chainable.
field('rooms').required().int().range(1, 20)
condition
: can throw errorfilter
: updates value
condition
condition
- nameOrFn:
String|function
- requiredValue:
any
(optional) used only if nameOrFn is string
filter
- The field is removed if value is undefined.
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.
filter
- The same as optional but only if request's method is PATCH.
filter
- Returned value will be an Array.
filter
- children:
Array
- Creates container will children fields, can be combined with
field.array()
.
filter
- Converts value to array via splitting by separator.
condition
,filter
condition
,filter
- requiredValue:
Boolean
(optional) - Converts to boolean and if
requiredValue
is provided, the field's value must be equal to it.
condition
,filter
- requiredValue:
Number
(optional) - Converts to number (0, 1) and if
requiredValue
is provided, the field's value must be equal to it.
condition
- compare:
any
filter
- defaultValue:
any
filter
- Converts to null if is empty.
condition
condition
,filter
filter
filter
filter
filter
- length:
Number
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)
condition
,filter
- min:
Number|String|Function
(can be a number or name of a field or function that returns number)
condition
,filter
- max:
Number|String|Function
(can be a number or name of a field or function that returns number)
condition
- min:
Number
- max:
Number
condition
- min:
Number
condition
- max:
Number
condition
,filter
condition
,filter
condition
,filter
condition
- options:
Array
filter
- Gets file from
req.files
object.
condition
- types:
Array
condition
- limit:
Number
in Bytes
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;
}
});
const datalize = require('datalize');
const field = datalize.field;
datalize.Field.prototype.isSlug = function(chars = 'a-z-') {
const regexp = new RegExp(`^([${chars}]+)$`);
// 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()
]);
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,
};
});
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());
}
}
});
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()));
}
});
MIT
Copyright (c) 2018 Andrej Adamcik