Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript - Creating custom express middleware definition

I've been having a real difficult time trying to define custom middleware for our app. I'm using [email protected] and [email protected].

All the examples I've been seeing for typing middleware implies that the user is not adding anything to the req or res argument, but I know that that was kind of the point of middleware - mutate either one of those objects knowing that the middlewares had to be loaded in a certain order (ie. cookie-parser or body-parser was notorious for this in the early days, i guess still is?). Anyway, most of the examples net out to something like this github issue: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/26146#issuecomment-393386416

The problem I have with that is that we do mutate the req and res objects being passed along to each middleware so I've had to type both objects accordingly:

import {
    Express as ExpressInterface,
    Request as ExpressRequest,
    Response as ExpressResponse,
    NextFunction as ExpressNextFunction,
} from 'express';
import LoggerType from 'some-log-package';

// don't do anything different the `next()`
export type ExpressNextFunction = ExpressNextFunction;

export interface CustomServerApp extends ExpressInterface {
    customHeader: string;
    customLogger: LoggerType;
}

export interface CustomServerRequest extends ExpressRequest {
    app: CustomServerApp,
    id: string,
    urlSearchParams: URLSearchParams,
    // etc
}

export interface CustomServerResponse extends ExpressResponse { ... }

So I export the basic arguments for middleware:

  • req = CustomServerRequest
  • res = CustomServerResponse
  • next = ExpressNextFunction

Of course, I import those accordingly to my middleware files:

import {
    CustomServerRequest,
    CustomServerResponse,
    ExpressNextFunction
} from './server-types'

export const customMiddleware = (req: CustomServerRequest, res: CustomServerResponse, next: ExpressNextFunction): void {
    // do something here with either `req` or `res` or both!
    next()
};

We have a centralized middleware.ts file where we put all of our global middleware into so it looks something like this:

import { CustomServerApp } from './server-types';
import { customMiddleware } from './middlewares/customMiddleware';
export const middlewares = (app: CustomServerApp): void => {
    app.use(
        customMiddleware,
        // maybe more middlewares, etc
    );
}

However, then the type checker runs, I get errors like the following:

No overload matches this call.
  The last overload gave the following error.
    Argument of type '(req: Request, res: Response, next: NextFunction) => void' is not assignable to parameter of type 'PathParams'.
      Type '(req: Request, res: Response, next: NextFunction) => void' is missing the following properties from type '(string | RegExp)[]': pop, push, concat, join, and 27 more.ts(2769)

I've been having a real tough time understanding why this is getting caught on the last overload definition. Just based on the @types/express, it seems hard to add an additional overload to middleware handler even with type declaration merging.

Even when I follow a similar pattern to the core express types to define a middleware handler - https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express-serve-static-core/index.d.ts#L40-L43 - it doesn't work out either. It also doesn't seem like it's easy to extend from either - https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express/index.d.ts#L99 - since you can't pass a generic to the core RequestHandler, unlike the router definitions (IRouterHandler, IRouterMatcher). When I try and type it this way, I get different, but similar type errors.

Has anyone else experienced doing something similar?

like image 510
hellatan Avatar asked Oct 08 '19 00:10

hellatan


People also ask

How does Express define middleware?

Middleware functions are functions that have access to the request object ( req ), the response object ( res ), and the next function in the application's request-response cycle. The next function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.

Can Express be written in TypeScript?

With TypeScript configured, we can create the Express web server. First, create the file index. ts (attention to the file extension) by running touch index.

Which is correct syntax for writing middleware in Express JS?

Answer: C is the correct option. The function(req,res,next){ } is a middleware in Express. js.


1 Answers

You can't add a required field in your req and res variable, because express can't satisfy the constraint for you. Also, express doesn't handle changing req/res type after passing through different middleware. So, your custom type can contain required fields the same that in ExpressRequest/ExpressResponse, the rest fields should be optional and should have nullcheck inside the middleware.

like image 182
Roman Valihura Avatar answered Oct 07 '22 09:10

Roman Valihura