Nominally Patriotic Meathead

    datalize

    0.3.4 • Public • Published

    Datalize

    Parameter, query, form data validation and filtering for Koa and Express.

    See also this post about it on the Toptal Engineering Blog.

    npm npm license vulnerabilities dependencies

    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 error
    • filter: 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 requiredValue is 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 requiredValue is 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.files object.

    field.mime(types)

    • condition
    • types: Array

    field.size(limit)

    • condition
    • limit: Number 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

    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()
    ]);

    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

    Install

    npm i datalize

    DownloadsWeekly Downloads

    28

    Version

    0.3.4

    License

    none

    Unpacked Size

    27.3 kB

    Total Files

    8

    Last publish

    Collaborators

    • adamcikado