Giter Site home page Giter Site logo

blakeembrey / compose-middleware Goto Github PK

View Code? Open in Web Editor NEW
91.0 6.0 8.0 532 KB

Compose an array of middleware into a single function for use in Express, Connect, router, etc

License: MIT License

TypeScript 100.00%
middleware express compose combine router connect

compose-middleware's Introduction

Compose Middleware

NPM version NPM downloads Build status Test coverage

Compose an array of middleware into a single function for use in Express, Connect, router, etc.

Installation

npm install compose-middleware --save

Usage

Compose multiple middleware functions into a single request middleware handler, with support for inline error handling middleware.

var express = require("express");
var compose = require("compose-middleware").compose;

var app = express();

app.use(
  compose([
    function(req, res, next) {},
    function(err, req, res, next) {},
    function(req, res, next) {}
  ])
);

P.S. The composed function takes three arguments. Express.js (and Connect, router) only accept error handlers of four arguments. If you want to return an error handler from compose instead, try the errors export - it works exactly the same, but exposes the four argument middleware pattern.

License

MIT

compose-middleware's People

Contributors

blakeembrey avatar dependabot[bot] avatar greenkeeperio-bot avatar whymarrh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

compose-middleware's Issues

doesn't compile with --strictNullChecks

$ tsc --version
Version 2.8.1

try to compile this code with "strictNullChecks: true":

import { Request as ExpressRequest, 
         Response as ExpressResponse, 
         Handler as ExpressHandler,
         ErrorRequestHandler as ExpressErrorHandler,
         NextFunction } from 'express';

compose<ExpressRequest, ExpressResponse, any>([
        (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
          next();
        }
]);

You will get the error:

$ tsc
src/core/controller.ts(180,53): error TS2345: Argument of type '((req: Request, res: Response, next: NextFunction) => void)[]' is not assignable to parameter of type 'Handler<Request, Response, any>'.
  Type '((req: Request, res: Response, next: NextFunction) => void)[]' is not assignable to type 'NestedArray<Middleware<Request, Response, any>>'.
    Types of property 'push' are incompatible.
      Type '(...items: ((req: Request, res: Response, next: NextFunction) => void)[]) => number' is not assignable to type '(...items: Handler<Request, Response,any>[]) => number'.
        Types of parameters 'items' and 'items' are incompatible.
          Type 'Handler<Request, Response, any>' is not assignable to type '(req: Request, res: Response, next: NextFunction) => void'.
            Type 'ErrorHandler<Request, Response, any>' is not assignable to type '(req: Request, res: Response, next: NextFunction) => void'.
              Types of parameters 'err' and 'req' are incompatible.
                Type 'Request' is not assignable to type 'Error | undefined'.
                  Type 'Request' is not assignable to type 'Error'.
                    Property 'name' is missing in type 'Request'.

But, if you set "strictNullChecks: false", it's fine!

using `compose` for returning 3 and 4 arity middlewares

Hi @blakeembrey

I find your package and it's just what I was looking for. I really like this package because it can helps to split my middlewares into little pieces and compose them. My congrats!

However I would like to understand why is errors function needed. I think it's possible to have just one compose function:

  • If the first middleware has less than 4 arguments, then it returns a 3-arity middleware.
  • If the first middleware has 4 arguments, then it returns a 4-arity middleware.

I'm basing this proposal on that usual composition returns a function with the same arity than first called function.

What do you think?

Document clarification re: error handling

First off, excellent module! This thing is well built and extremely useful. Thank you for making it more enjoyable to build http servers in node.

I have a small nit that I think may be worth pointing out in the README to possibly save future devs some pain, and that's this:

99% of the time, devs can assume that this module will create a single piece of middleware that behaves equivalent to all composed middleware.

There is at least one case though where this is not true:

// without-compose.js
app.use(
  (req, res, next) =>      { res.write('a'); next() }
)

app.use(
  (req, res, next) =>      { next(new Error('bummer!')) }
)

// here's our chain of error-handling middleware
app.use([
  (err, req, res, next) => { res.write('b'); next(err) },
  (err, req, res, next) => { res.write('c'); next(err) },
])

app.use(
  (err, req, res, next) => { res.write('d'); next(err) }
)

In this example, the caller will receive a response of abcd.

If however, we use compose in the above example to compose the array of error handling middleware, the result is different:

// with-compose.js
app.use(
  (req, res, next) =>      { res.write('a'); next() }
)

app.use(
  (req, res, next) =>      { next(new Error('bummer!')) }
)

// this chain acts differently when used with compose
app.use(compose([
  (err, req, res, next) => { res.write('b'); next(err) },
  (err, req, res, next) => { res.write('c'); next(err) },
]))

app.use(
  (err, req, res, next) => { res.write('d'); next(err) }
)

This will result in a response of ad.

As far as I'm aware, there's nothing we can really do about this. The reason for this behavior is probably very obvious to anyone who has seen the source code: compose always returns a function with a signature of (req, res, next), which, requiring 3 arguments and not 4, will result in express only registering the composed function for success handlers and not error handlers.

The TL;DR that I think warrants noting in the README is this: if devs wish to produce a composed function specifically for handling errors, they will have to do it some other way as the signature of the composed function that they end up registering will need to require 4 arguments and not 3.

Thanks for taking the time to give this a read! And thanks for building a great module!

Invalid type inference with flatten.NestedArray

Using v 5.0.0

Problem description

In the following snippet, typescript template arguments are not inferred correctly.

I expose the compose fn as pipeMiddleware, accepting flat array spreaded arguments:

import { Request, Response } from 'express';
import { compose, Middleware } from 'compose-middleware';

const pipeMiddleware = (...middlewares: Array<Middleware<Request, Response>>) => compose(middlewares);

This works in [email protected] flawlessly, however, in current [email protected] it produces the following error:

error TS2345: Argument of type 'Middleware<Request, Response, void>[]' is not assignable to parameter of type 'Handler<Error, Request, void>'.
  Type 'Middleware<Request, Response, void>[]' is not assignable to type 'Middleware<Error, Request, void>[]'.
    Type 'Middleware<Request, Response, void>' is not assignable to type 'Middleware<Error, Request, void>'.
      Type 'RequestHandler<Request, Response, void>' is not assignable to type 'Middleware<Error, Request, void>'.
        Type 'RequestHandler<Request, Response, void>' is not assignable to type 'RequestHandler<Error, Request, void>'.
          Types of parameters 'req' and 'req' are incompatible.
            Type 'Error' is not assignable to type 'Request'.

Notice the first line, type template parameters (Handler<Error, Request, void>) are incorrectly inferred.

Temp solution

Declaring template arguments explicitly does the trick for my case.

const pipe = (...middlewares: Array<Middleware<Request, Response>>) => compose<Request, Response>(middlewares);

Assumed cause

This might be a bug in flatten types.

If compose-middleware's type Handler is changed from

type Handler<T, U, V = void> = Middleware<T, U, V> | flatten.NestedArray<Middleware<T, U, V>>;

to

type Handler<T, U, V = void> = Middleware<T, U, V> | Array<Middleware<T, U, V>>;

it works flawlessly again (I use only flat simple arrays in my app.)

Type 'Next<void>' is not assignable to type 'NextFunction'

I am using Typescript v4.0.2 and I am getting a type error when trying to use third party middleware that uses the NextFunction type from @types/express.

Culprit code:

// Next & NextFunction are not compatable and no way to overwrite with generic
type Next<T = void> = (err?: Error | null) => T;

type RequestHandler<T, U, V = void> = (req: T, res: U, next: Next<V>) => V;

I recommend something like:

type RequestHandler<TRequest, TResponse, TNext, TReturn> = (req: TRequest, res: TResponse, next: TNext) => TReturn;

How to use with http.createServer?

This seems to work, but I'm not sure if I'm missing anything (like error handling):

const { compose } = require('compose-middleware')
const { createServer } = require('http')

const middleware = []
const handler = compose(middleware)

const server = createServer((req, res) => {
  handler(req, res, () => res.end())
})

Thanks for creating this package @blakeembrey!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.