npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

middlewall

v0.3.0

Published

Modulated validation library for express

Downloads

8

Readme

Middlewall

Build Status npm

With Middlewall you can build plugable validation middlewares, Separate all validation functionality to different layer than your business logic, making a leaner, reusable, more readable code.

Key Features :

  • Growing collection of common validation methods, referred as operations.
  • parse and transform incoming request while validating (support success callback use as a window for parsing).
  • descriptive error massages.
  • Custom async validations

Quick Overview

Working with Middlewall involve using

  • operations - validations methods
  • operators - activate and manipulate the validation methods

In a nutshell, with the given operators you're composing a single block of validation from multiple individual validation operations then you can reuse that single block validation or transforming it to a middleware.

Usage

Let say you're building an API that support reading shows, artists and venues all with pagination, each of the readable item type implemented on an individual controller.

Pagination in your API implemented with a single protocol, using 'page' and 'pageSize' provided as a query parameters,

in the following code piece we're creating a middleware to validate and parse 'page' and 'pageSize' parameters, it will be plugged before each API call used for reading.


import { compose, goTo, each } from 'middlewall';
import * as ops from 'middlewall';

const app = express();

app.use((err, req, res, next) => {
    /*  any found validation errors will end up here ..
        for example: for the call '/show?page=-3&pageSize=100', err will look like :
        {
            "pass": false,
            "errors": [
                {
                    "pass": false,
                    "error": "value is not pass as a positive number",
                    "value": -3,
                    "path": "query.page",
                    "validation": "isPositive"
                }
            ]
        } 
    */
});


const paginationValidator = compose(
    // validate query.page is a string-number and if so parse it
    ops.isIntegerString('page',                     // point to the target field            
        (page, req) => parseInt(page) || 1,         // provide an if-pass cb
        undefined,                                  // a costume error massage can be provided 
        { overwriteValue: true, optional: true, default: 1 }    // setting field as optional and overwrite it with if-pass cb return value
    ),  
    ops.isPositive('page', undefined, undefined, { optional: true }),

    // validate query.pageSize is a string-number and if so parse it
    ops.isIntegerString('pageSize', 
        (pageSize, req) => parseInt(pageSize), 
        undefined, 
        { overwriteValue: true, optional: true, default: 20 }),
    ops.isBetween('pageSize', 1, 100),
).query(); // top target object is req.query.


app.get('/show', paginationValidator, (req, res, next) => {
        /*  pass all validations! continue your flow */
    }
);

// and so on for '/artist' and '/venue' calls ..

    

Continue building your API validation layer, your API provides a filtering functionality base on dates, the parameters 'start' and 'end' provided with the range dates.

in the following code piece we're creating a middleware to validate the dates range could be something like :

static dateValidator = compose(
    ops.isDateString('start', 'mm-dd-yyyy',    
        (start, req) => moment(start, 'mm-dd-yyyy'),        
        undefined, 
        { overwriteValue: true, optional: true, default: moment().year(1970) }),
    ops.isDateString('end', 'mm-dd-yyyy',
        (end, req) => moment(end, 'mm-dd-yyyy'),        
        undefined, 
        { overwriteValue: true, optional: true, default: moment().year(2100) }),
).query(); // top target object is req.query. 


app.get('/show', paginationValidator, dateValidator, (req, res, next) => {
        /*  pass all validations! continue your flow 
            for example: for the call '/show?page=1&start=04-20-2015, req.query will look like :
            {
                "page": 1,
                "start": "2019-08-08T21:04:00.000Z",
                "end": "2100-08-09T11:10:43.251Z",
                "pageSize": 20
            }
        */

    }
);

Now your API support post content functionalities, for that an authorization process must take place,

in the following code piece we're creating a custom authorization middleware for the incoming request and validate posed content.


const authHeaderValidator = compose(
    ops.isExist('req.headers.authorization'), 
    ops.isStartWith(
        'req.headers.authorization', 'Bearer ', 
        (auth: string) => auth.slice('Bearer '.length), 
        undefined, { overwriteValue: true }
    ),
    ops.run('', // empty string meaning stay on the root object.
        async ({ req, res }) => { 
            try {
                const { authorization } = req.headers;
                const decodedToken = await firebaseAdmin.auth().verifyIdToken(authorization);
                if(!decodedToken || !decodedToken.uid) {
                    return false;
                }
                res.locals.uid = decodedToken.uid;
                return true;
            } catch (error) {
                return false;
            } 
        }, undefined, 'Authentication failed'
    )
).args();

const showListValidator = compose(
    ops.isArray('shows'),   // validate body.shows is an array
    goTo('shows',           // navigate to body.shows 
        each(               // perform the validations list on each of the items in the 'shows' array
            ops.isAlpha('name'),
            ops.isDateString('showDate', 'mm-dd-yyyy'),
            ops.isBoolean('visible', undefined, undefined, { optional: true, default: true })
        )
    )
).body(); // top target object is req.body.


app.post('/show', authHeaderValidator, showListValidator, (req, res, next) => {
        /*  pass all validations! continue your flow */
    }
);

Api Reference

Middlewall

Middlewall encapsulate a collection of validation operations in a middleware compatible with express API.

  • Middlewall.prototype.args() generate a middleware where the validation root object is an object structured {req: Request res: Response}.

  • Middlewall.prototype.req() generate a middleware where the validation root object is the incoming request object.

  • Middlewall.prototype.body() generate a middleware where the validation root object is the incoming request.body object.

  • Middlewall.prototype.query() generate a middleware where the validation root object is the incoming request.query object.

  • Middlewall.prototype.params() generate a middleware where the validation root object is the incoming request.params object.

  • Middlewall.prototype.headers() generate a middleware where the validation root object is the incoming request.headers object.

  • Middlewall.prototype.locals() generate a middleware where the validation root object is the incoming response.locals object.

Note:

  • Middleware class was not intended to be instantiate directly, instead use compose operator (see Usage section)

Operators

Note :

  • brick refer to validation operation or an object / function of type AsyncBrickFn, the bricks are used for build up your middlewall.

Validation operators used for customize the validation operations.

  • compose(...bricks: Array<AsyncBrickFn | Middlewall>): Middlewall Use to compose one or more validation operations to a Middlewall, function as a logical and.

  • and(...bricks: Array<AsyncBrickFn | Middlewall>): AsyncBrickFn Use to reduce one or more validation to a single validation operation (a brick), function as a logical and.

  • or(...bricks: Array<AsyncBrickFn | Middlewall>): AsyncBrickFn Use to reduce one or more validation to a single validation operation (a brick), function as a logical or.

  • goTo(path: string, ...bricks: Array<AsyncBrickFn | Middlewall>): AsyncBrickFn Use to navigate inside the target object.

  • each(...bricks: Array<AsyncBrickFn | Middlewall>): AsyncBrickFn Use to perform validation on each item in an array, where only all the items must pass the validation.

  • some(...bricks: Array<AsyncBrickFn | Middlewall>): AsyncBrickFn Use to perform validation on each item in an array, where only one item must pass the validation.

Operations

Operations follow the same API structure in general, different operations can receive different amount of parameters with different types. The general structure: operation(path: string, args1: any, args2: any, ... , ifPass?: IfPassFn, options?: ValidationOptions)

  • path: string
    • path to the target field from the current context location.
    • required
  • ...args: Array<any>
    • extra arguments for the validation operation.
    • some operations required the argument by logic, some aren't.
  • ifPass?: IfPassFn
    • callback function that execute if the validation passes.
    • optional
  • error?: string
    • custom error massage.
    • optional
  • options?: ValidationOptions
    • option for the validation operation.
    • optional

interface ValidationOptions

fields :

  • optional

    • type: boolean
    • description : ignore validation result if value is undefined or null, validation action still run.
    • required: no
  • overwriteValue

    • type: boolean
    • description : overwrite the target value with the return value of is-pass callback.
    • required: no
  • default

    • type: any
    • description : set the target to the provided value if 'optional' set to true and target value not exists.
    • required: no

function IfPassFn

arguments :

  • target

    • type: any
    • description : the target field on which the validation performed on.
  • root

    • type: boolean
    • description : the top level parent object, were the target field is derived from.

Validation Operation

soon ..