Extend Express Request object using Typescript

node.jsExpressTypescript

node.js Problem Overview


I’m trying to add a property to express request object from a middleware using typescript. However I can’t figure out how to add extra properties to the object. I’d prefer to not use bracket notation if possible.

I’m looking for a solution that would allow me to write something similar to this (if possible):

app.use((req, res, next) => {
    req.property = setProperty(); 
    next();
});

node.js Solutions


Solution 1 - node.js

You want to create a custom definition, and use a feature in Typescript called Declaration Merging. This is commonly used, e.g. in method-override.

Create a file custom.d.ts and make sure to include it in your tsconfig.json's files-section if any. The contents can look as follows:

declare namespace Express {
   export interface Request {
      tenant?: string
   }
}

This will allow you to, at any point in your code, use something like this:

router.use((req, res, next) => {
    req.tenant = 'tenant-X'
    next()
})

router.get('/whichTenant', (req, res) => {
    res.status(200).send('This is your tenant: '+req.tenant)
})

Solution 2 - node.js

As suggested by the comments in the index.d.ts, you simply declare to the global Express namespace any new members. Example:

declare global {
  namespace Express {
    interface Request {
      context: Context
    }
  }
}
Full Example:
import * as express from 'express';

export class Context {
  constructor(public someContextVariable) {
  }

  log(message: string) {
    console.log(this.someContextVariable, { message });
  }
}

declare global {
  namespace Express {
    interface Request {
      context: Context
    }
  }
}

const app = express();

app.use((req, res, next) => {
  req.context = new Context(req.url);
  next();
});

app.use((req, res, next) => {
  req.context.log('about to return')
  res.send('hello world world');
});

app.listen(3000, () => console.log('Example app listening on port 3000!'))

Extending global namespaces is covered more at my GitBook.

Solution 3 - node.js

For newer versions of express, you need to augment the express-serve-static-core module.

This is needed because now the Express object comes from there: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19

Basically, use the following:

declare module 'express-serve-static-core' {
  interface Request {
    myField?: string
  }
  interface Response {
    myField?: string
  }
}

Solution 4 - node.js

After trying 8 or so answers and not having a success. I finally managed to get it working with jd291's comment pointing to 3mards repo.

Create a file in the base called types/express/index.d.ts. And in it write:

declare namespace Express {
    interface Request {
        yourProperty: <YourType>;
    }
}

and include it in tsconfig.json with:

{
    "compilerOptions": {
        "typeRoots": ["./types"]
    }
}

Then yourProperty should be accessible under every request:

import express from 'express';

const app = express();

app.get('*', (req, res) => {
    req.yourProperty = 
});

Solution 5 - node.js

The accepted answer (as the others) does not works for me but

declare module 'express' {
    interface Request {
        myProperty: string;
    }
}

did. Hope that will help someone.

Solution 6 - node.js

None of the offered solutions worked for me. I ended up simply extending the Request interface:

import {Request} from 'express';

export interface RequestCustom extends Request
{
    property: string;
}

Then to use it:

import {NextFunction, Response} from 'express';
import {RequestCustom} from 'RequestCustom';

someMiddleware(req: RequestCustom, res: Response, next: NextFunction): void
{
    req.property = '';
}

Edit: Depending on your tsconfig, you may need to do it this way instead:

someMiddleware(expressRequest: Request, res: Response, next: NextFunction): void
{
    const req = expressRequest as RequestCustom;
    req.property = '';
}

Solution 7 - node.js

Alternative solution

This is not actually answering to the question directly, but I'm offering an alternative. I was struggling with the same problem and tried out pretty much every interface extending solution on this page and none of them worked.

That made me stop to think: "Why am I actually modifying the request object?".

Use response.locals

Express developers seem to have thought that users might want to add their own properties. That's why there is a locals object. The catch is, that it's not in the request but in the response object.

The response.locals object can contain any custom properties you might want to have, encapsulated in the request-response cycle, thus not exposed to other requests from different users.

Need to store an userId? Just set response.locals.userId = '123'. No need to struggle with the typings.

The downside of it is that you have to pass the response object around, but it's very likely that you are doing it already.

https://expressjs.com/en/api.html#res.locals

Typing

Another downside is the lack of type safety. You can, however, use the generic types on the Response object to define what's the structure of the body and the locals objects:

Response<MyResponseBody, MyResponseLocals>

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express/index.d.ts#L127

Caveats

You cannot really guarantee that the userId property is actually there. You might want to check before accessing it, especially in case of objects.

Using the example above to add an userId, we could have something like this:

interface MyResponseLocals {
  userId: string;
}

const userMiddleware = (
  request: Request,
  response: Response<MyResponseBody, MyResponseLocals>,
  next: NextFunction
) => {
  const userId: string = getUserId(request.cookies.myAuthTokenCookie);
  // Will nag if you try to assign something else than a string here
  response.locals.userId = userId;
  next();
};

router.get(
  '/path/to/somewhere',
  userMiddleware,
  (request: Request, response: Response<MyResponseBody, MyResponseLocals>) => {
    // userId will have string type instead of any
    const { userId } = response.locals;

    // You might want to check that it's actually there
    if (!userId) {
      throw Error('No userId!');
    }
    // Do more stuff
  }
);

Solution 8 - node.js

All of these responses seem to be wrong or outdated in some way or another.

This worked for me in May, 2020:

in ${PROJECT_ROOT}/@types/express/index.d.ts:

import * as express from "express"

declare global {
	namespace Express {
		interface Request {
			my_custom_property: TheCustomType
		}
	}
}

in tsconfig.json, add / merge the property such that:

"typeRoots": [ "@types" ]

Cheers.

Solution 9 - node.js

While this is a very old question, I stumbled upon this problem lately.The accepted answer works okay but I needed to add a custom interface to Request - an interface I had been using in my code and that didn't work so well with the accepted answer. Logically, I tried this:

import ITenant from "../interfaces/ITenant";

declare namespace Express {
    export interface Request {
        tenant?: ITenant;
    }
}

But that didn't work because Typescript treats .d.ts files as global imports and when they have imports in them they are treated as normal modules. That is why the code above doesn't work on a standard typescript setting.

Here's what I ended up doing

// typings/common.d.ts

declare namespace Express {
    export interface Request {
        tenant?: import("../interfaces/ITenant").default;
    }
}
// interfaces/ITenant.ts

export interface ITenant {
    ...
}

Solution 10 - node.js

With express 4.17.1 the combination of https://stackoverflow.com/a/55718334/9321986 and https://stackoverflow.com/a/58788706/9321986 worked:

in types/express/index.d.ts:

declare module 'express-serve-static-core' {
    interface Request {
        task?: Task
    }
}

and in tsconfig.json:

{
    "compilerOptions": {
        "typeRoots": ["./types"]
    }
}

Solution 11 - node.js

This is what worked for me when using Nestjs and Express. As on Nov 2020.

Create a file: ./@types/express-serve-static-core/index.d.ts

Note: must have exactly the above path and file name. So that Typescript declaration merging will work.

import { UserModel } from "../../src/user/user.model";

declare global{
    namespace Express {
        interface Request {
            currentUser: UserModel
        }
    }
}

add this to your tsconfig.json

"typeRoots": [
      "@types",
      "./node_modules/@types",
    ]        

Solution 12 - node.js

In TypeScript, interfaces are open ended. That means you can add properties to them from anywhere just by redefining them.

Considering that you are using this express.d.ts file, you should be able to redefine the Request interface to add the extra field.

interface Request {
  property: string;
}

Then in your middleware function, the req parameter should have this property as well. You should be able to use it without any changes to your code.

Solution 13 - node.js

If you are looking for solution that works with express4, here it is:

@types/express/index.d.ts: --------must be /index.d.ts

declare namespace Express { // must be namespace, and not declare module "Express" { 
  export interface Request {
    user: any;
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2016",
    "typeRoots" : [
      "@types", // custom merged types must be first in a list
      "node_modules/@types",
    ]
  }
}

Ref from https://github.com/TypeStrong/ts-node/issues/715#issuecomment-526757308

Solution 14 - node.js

This answer will be beneficial to those who rely on npm package ts-node.

I was also struggling with the same concern of extending request object, I followed a lot of answers in stack-overflow & ended with following the below-mentioned strategy.

I declared extended typing for express in the following directory. ${PROJECT_ROOT}/api/@types/express/index.d.ts

declare namespace Express {
  interface Request {
    decoded?: any;
  }
}

then updating my tsconfig.json to something like this.

{
  "compilerOptions": {
     "typeRoots": ["api/@types", "node_modules/@types"]
      ...
  }
}

even after doing the above steps, the visual studio stopped complaining, but unfortunately, the ts-node compiler still used to throw.

 Property 'decoded' does not exist on type 'Request'.

Apparently, the ts-node was not able to locate the extended type definitions for request object.

Eventually after spending hours, as I knew the VS Code was not complaining & was able to locate the typing definitions, implying something is wrong with ts-node complier.

Updating start script in package.json fixed it for me.

"start": "ts-node --files api/index.ts",

the --files arguments play a key role here find determining the custom type definitions.

For further information please visit: https://github.com/TypeStrong/ts-node#help-my-types-are-missing

Solution 15 - node.js

One possible solution is to use "double casting to any"

1- define an interface with your property

export interface MyRequest extends http.IncomingMessage {
     myProperty: string
}

2- double cast

app.use((req: http.IncomingMessage, res: http.ServerResponse, next: (err?: Error) => void) => {
    const myReq: MyRequest = req as any as MyRequest
    myReq.myProperty = setProperty()
    next()
})

The advantages of double casting are that:

  • typings is available
  • it does not pollute existing definitions but extends them, avoiding confusion
  • since the casting is explicit, it compiles fines with the -noImplicitany flag

Alternatively, there is the quick (untyped) route:

 req['myProperty'] = setProperty()

(do not edit existing definition files with your own properties - this is unmaintainable. If the definitions are wrong, open a pull request)

EDIT

See comment below, simple casting works in this case req as MyRequest

Solution 16 - node.js

To help anyone who is just looking for something else to try here is what worked for me in late May of 2020 when trying to extend ExpressJS' Request. I had to have tried more than a dozen things before getting this to work:

  • Flip the order of what everyone is recommending in the "typeRoots" of your tsconfig.json (and don't forget to drop the src pathing if you have a rootDir setting in tsconfig such as "./src"). Example:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • Example of custom extension ('./your-custom-types-dir/express/index.d.ts"). I had to use inline import and default exports to use classes as a type in my experience so that is shown too:
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • Update your nodemon.json file to add the "--files" command to ts-node, example:
{
  "restartable": "rs",
  "ignore": [".git", "node_modules/**/node_modules"],
  "verbose": true,
  "exec": "ts-node --files",
  "watch": ["src/"],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js,json,ts"
}

Solution 17 - node.js

Maybe this issue has been answered, but I want to share just a little, now sometimes an interface like other answers can be a little too restrictive, but we can actually maintain the required properties and then add any additional properties to be added by creating a key with a type of string with value type of any

import { Request, Response, NextFunction } from 'express'

interface IRequest extends Request {
  [key: string]: any
}

app.use( (req: IRequest, res: Response, next: NextFunction) => {
  req.property = setProperty();
  
  next();
});

So now, we can also add any additional property that we want to this object.

Solution 18 - node.js

If you tried all the answers and still didn't get it to work, here is a simple hack

app.use((req, res, next) => {
    (req as any).property = setProperty(); 
    next();
});

This will cast the req object to any, therefore you can add any property you want. Keep in mind that by doing this you will lose type safety with req object.

Solution 19 - node.js

I used response.locals object to store the new property. Here is the complete code

export function testmiddleware(req: Request, res: Response, next: NextFunction) {
    res.locals.user = 1;
    next();
}

// Simple Get
router.get('/', testmiddleware, async (req: Request, res: Response) => {
    console.log(res.locals.user);
    res.status(200).send({ message: `Success! ${res.locals.user}` });
});

Solution 20 - node.js

I have same problem and resolve it like this:

// /src/types/types.express.d.ts
declare namespace Express {
    export interface Request {
        user: IUser
    }
}

But some conditions are required!

  1. Add to tsconfig.json config
"paths": {
    "*": [
        "node_modules/*",
        "src/types/*"
    ]
},

After this tsc will build bundle, but ts-node not.

  1. You must add --files to compiler command
ts-node --files src/server.ts

Solution 21 - node.js

on mac with node 12.19.0 and express 4, using Passport for auth, I needed to extend my Session object

similar as above, but slightly different:

import { Request } from "express";


declare global {
  namespace Express {
    export interface Session {
      passport: any;
      participantIds: any;
      uniqueId: string;
    }
    export interface Request {
      session: Session;
    }
  }
}

export interface Context {
  req: Request;
  user?: any;
}```

Solution 22 - node.js

Simple solution which worked for me is to create a new custom interface extending express Request. This interface should contain all your custom req properties as optional. Set this interface as type for the middleware req.

// ICustomRequset.ts
   import { Request } from "express"
   export default interface ICustomRequset extends Request {
       email?: string;
       roles?: Array<string>;
   }

// AuthMiddleware.ts
...
export default async function (
  req: ICustomRequset,
  res: Response,
  next: NextFunction
) {
  try {
      // jwt code
      req.email=jwt.email
      req.roles=jwt.roles
      next()
  }catch(err){}

Solution 23 - node.js

I solved this problem by creating a new type without extending the Request type globally.

import { Request } from 'express'
    
type CustomRequest = Request & { userId?: string }

You must use extend properties with optional(?) operator to not have 'No overload matches this call' error.

Package Versions :

    "@types/express": "^4.17.13",
    "@types/morgan": "^1.9.3",
    "@types/node": "^17.0.29",
    "typescript": "^4.6.3",
    "express": "^4.18.0",

Solution 24 - node.js

It might be already quite late for this answer, but anyway here is how I solved:

  1. Make sure you have your types source included in your tsconfig file (this could be a whole new thread)
  2. Inside your types directory add a new directory and name it as the package you want to extend or create types for. In this specific case, you will create a directory with the name express
  3. Inside the express directory create a file and name it index.d.ts (MUST BE EXACTLY LIKE THAT)
  4. Finally to make the extension of the types you just need to put a code like the following:
declare module 'express' {
    export interface Request {
        property?: string;
    }
}

Solution 25 - node.js

This worked for me:

declare namespace e {
    export interface Request extends express.Request {
        user:IUserReference,
        [name:string]:any;
    }
    export interface Response extends express.Response {
        [name:string]:any;
    }
}



export type AsyncRequestHandler = (req:e.Request, res:e.Response, logger?:Logger) => Promise<any>|Promise<void>|void;
export type AsyncHandlerWrapper = (req:e.Request, res:e.Response) => Promise<void>;

And I used it in code like exporting a function with such signatures in such way:

app.post('some/api/route', asyncHandlers(async (req, res) => {
        return await serviceObject.someMethod(req.user, {
            param1: req.body.param1,
            paramN: req.body.paramN,
            ///....
        });
    }));

Solution 26 - node.js

Simply add the property to req.params object.

req.params.foo = "bar"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIsak &#197;grenView Question on Stackoverflow
Solution 1 - node.jsmaximilianvpView Answer on Stackoverflow
Solution 2 - node.jsbasaratView Answer on Stackoverflow
Solution 3 - node.jsjonathancardosoView Answer on Stackoverflow
Solution 4 - node.jsBenView Answer on Stackoverflow
Solution 5 - node.jsmax-ltView Answer on Stackoverflow
Solution 6 - node.jsTom MettamView Answer on Stackoverflow
Solution 7 - node.jsTimoView Answer on Stackoverflow
Solution 8 - node.jsWill BricknerView Answer on Stackoverflow
Solution 9 - node.js16kbView Answer on Stackoverflow
Solution 10 - node.jsanliView Answer on Stackoverflow
Solution 11 - node.jsDavid DehghanView Answer on Stackoverflow
Solution 12 - node.jstoskvView Answer on Stackoverflow
Solution 13 - node.jsChandara CheaView Answer on Stackoverflow
Solution 14 - node.jsDivyanshu RawatView Answer on Stackoverflow
Solution 15 - node.jsBruno GriederView Answer on Stackoverflow
Solution 16 - node.jsIfTrueView Answer on Stackoverflow
Solution 17 - node.jsEka putraView Answer on Stackoverflow
Solution 18 - node.jsIdreesView Answer on Stackoverflow
Solution 19 - node.jssreejith v sView Answer on Stackoverflow
Solution 20 - node.jsMiFView Answer on Stackoverflow
Solution 21 - node.jsuser3325025View Answer on Stackoverflow
Solution 22 - node.jsAshiqSUltanView Answer on Stackoverflow
Solution 23 - node.jsruppyView Answer on Stackoverflow
Solution 24 - node.jsHector ManuelView Answer on Stackoverflow
Solution 25 - node.jsKote IsaevView Answer on Stackoverflow
Solution 26 - node.jsMilippView Answer on Stackoverflow