Reputation: 3847
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();
});
Upvotes: 363
Views: 278719
Reputation: 735
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"]
}
}
BTW: Think twice if you want to do this! I did this when I didn't know about TDD - now I can't imagine to not use tests for code in my daily work - and "poluting" the request object and letting it wander around in the application increases the chance that you need to mock the req for a lot of tests. Maybe it's better to read about Clean Code, SOLID, IOSP, IOSP 2.0 and IODA and things like that and understand why adding properties to the request to have it available "at the other end of the app" might not be the best idea.
Upvotes: 49
Reputation: 367
{root}/@types/express/index.d.ts
import { IUser } from "@/model/User";
declare global {
namespace Express {
interface Locals {
user: IUser | null;
}
}
}
in tscofig
update typeRoots
and include
:
{
"compilerOptions": {
"baseUrl": "src",
"outDir": "./dist", /* Specify an output folder for all emitted files. */
"rootDir": "./src",
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
"typeRoots": [
"./@types",
"./node_modules/@types",
],
},
"include": [
"src/**/*"
, "./@types/express" ],
}
export const getSessionHandler = asyncWrapper(async (req: Request, res: Response, next: NextFunction) => {
res.status(200).json({
data: {
user: res.locals.user,
},
});
});
Upvotes: 0
Reputation: 543
2024 update
I was trying to find modern way in a new project to extend Response.locals and tried almost everything in this thread again and had no luck (came here by Google search forgetting all about that I posted this answer before).
So I wanted to share what finally worked for me was:
// somePath/express/index.d.ts <--- Make sure it is in tsconfig "include"
import 'express';
interface Locals {
message?: string;
}
declare module 'express' {
export interface Response {
locals: Locals;
}
}
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:
"typeRoots": [
"./node_modules/@types",
"./your-custom-types-dir"
]
declare global {
namespace Express {
interface Request {
customBasicProperty: string,
customClassProperty: import("../path/to/CustomClass").default;
}
}
}
{
"restartable": "rs",
"ignore": [".git", "node_modules/**/node_modules"],
"verbose": true,
"exec": "ts-node --files",
"watch": ["src/"],
"env": {
"NODE_ENV": "development"
},
"ext": "js,json,ts"
}
Upvotes: 6
Reputation: 79
The accepted answer did well but I want to add more explanation for importing this definition file.
You should define the *.d.ts somewhere and also made this change to tsconfig file:
{
...
include: [
"somewhere pointing to your definition file or directory"
]
...
}
After that everything will go well
Upvotes: 0
Reputation: 821
I've usually followed one of the examples in the other answers, but in my latest project extending the Request type is not reliable. So I ended with a different solution that retains the per request middleware-to-middleware communication without monkey patching.
Initially set up a really small module using a WeakMap providing a middleware to initialise and a getter for the context later
import type { Request } from 'express'
export interface Context {
// Put whatever you're sharing in here
}
// Intentionally not exported to avoid shenanigans
const contextStore = new WeakMap<Request, Context>()
export function createContextMiddleware(request: Request) {
contextStore.set(request, {
// Initialise shared context
})
}
export function getContext(request: Request): Context | undefined {
return contextStore.get(request)
}
And the consuming middleware will use getContext(req)
and do a null check to avoid mishaps
export const myExcellentMiddleware = function (req, res) {
const ctx = getContext(req)
if (!ctx) {
// This should be handled as an 500 internal server error
throw new Error('Server error')
}
// Have fun coding ...
}
It depends on a global approach, but it acts like it's only available to the middleware per request because of the WeakMap. WeakMap is excellent here as you don't have to think about discarding unused values stored in it, because when GC events happen it will automatically remove it for you.
Depending on how much you trust the future contributors and review processes you can assume there will always be a context for each request by:
.get(request)!
),unedfined
union, andI for one make mistakes and prefer to leave strong reminders for good code.
Upvotes: 2
Reputation: 359
dep : "@types/express": "^4.17.17"
"typescript": "^5.1.6"
My goal is add __platform
as req object attribute and define req.body req.query type. Below is the specific code I want to use.
import Request from 'express';
interface QueryProps{
username: string;
}
interface BodyProps{
witdh: number;
height: number;
}
type IRequest = Request<object,object,BodyProps,QueryProps>
function testHandler(req:IRequest,res,next){
req.__platform // no error
req.query.username // no error
req.body.with // no error
req.body.height // no error
}
below ts type declare works for me.
// index.d.ts
import { Request as ExpressRequest } from 'express';
declare module 'express' {
interface Request extends ExpressRequest {
__platform: string;
}
}
declare module 'express-serve-static-core' {
interface Request {
__platform: string;
}
}
Upvotes: 1
Reputation: 355
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,
///....
});
}));
These options are possible:
async function getRequestUser(req:Request):Promise<ICustomUser> {
let currentUser:ICustomUser = req[UserSymbolProp] || null as ICustomUser;
// if user not set already, try load it, if no such user but header present, throw error?
return currentUser;
}
app.get('/api/get/something', async(req, res, next) => {
try {
let user = await getRequestUser(req);
//do something else
} catch(err) {
next(err);
}
});
Other option is quite similar, but you make single function that returns all custom context you need in your code:
function getAPIContext(req:Request):IAPIContext {
let ctx = req[APICtxSymbol] || null as IApiContext;
if (!ctx) {
ctx = prepareAPIContext(req);
req[APICtxSymbol] = ctx;
}
return ctx;
}
app.get('/api/to/get/something', async(req, res, next) => {
try {
let ctx = getAPIContext(req);
///use context somehow
let reply = await doSomething(ctx);
res.json(reply);
} catch(err) {
next(err);
}
}
Second approach is better, as you can create unit tests that use test-special implementation of context and make unit tests directly over doSomething (export that code, of course). Second construction can be reused via function like wrapHandler that accept real handling function like
function wrapHandler<T>(handler: (req:IAPIContext) => Promise<T>|T) => (req:Request, res:Response, next:NextFunction) => Promise<void>|void;
Upvotes: 0
Reputation: 66
Solution which finally worked for me with typescript 4.8.4 and express 4.18.2:
Taking this COMMENT and wrapping the whole thing in "declare global" like this:
declare global {
declare module 'express-serve-static-core' {
interface Request {
userId?: string;
}
}
}
File structure:
/typeDeclarations/express/index.d.ts
/tsconfig.json
I have also added path to my declarations to the tsconfig file, but everything also worked without it.
"typeRoots": [
"./node_modules/@types",
"./typeDeclarations"
],
Upvotes: 4
Reputation: 2186
The easiest method is to extend the type you want and add your own properties
in tsconfig.ts specify the root of local types
{
// compilerOptions:
"typeRoots": ["node_modules/@types", "**/@types"],
}
now create any .d.ts file inside @types, you can put @types in the root or anyware.
@types/express.d.ts
declare namespace Express {
interface Request {
// add arbitrary keys to the request
[key: string]: any;
}
}
Upvotes: 1
Reputation: 276239
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
}
}
}
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 also covered in the TypeScript Deep Dive.
Upvotes: 253
Reputation: 489
For simple case, I use the headers
property in outer middleware and get it later in inner middleware.
// outer middleware
req.headers["custom_id"] = "abcxyz123";
// inner middleware
req.get("custom_id");
drawbacks:
string
. if you want to store other types such as json
or number
, you might have to parse it later.headers
property is not documented. Express only documents the req.get()
method. So you have to use the exact version of Express that works with property headers
.Upvotes: 0
Reputation: 224
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",
Upvotes: 20
Reputation: 678
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!
tsconfig.json
config"paths": {
"*": [
"node_modules/*",
"src/types/*"
]
},
After this tsc
will build bundle, but ts-node
not.
--files
to compiler commandts-node --files src/server.ts
Upvotes: 5
Reputation: 1334
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}` });
});
Upvotes: 2
Reputation: 41
Simply add the property to req.params object.
req.params.foo = "bar"
Upvotes: -5
Reputation: 24775
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",
]
Upvotes: 13
Reputation: 2963
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 = '';
}
Upvotes: 55
Reputation: 2989
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?".
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
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
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
}
);
Upvotes: 40
Reputation: 115
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.
Upvotes: 5
Reputation: 81
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){}
Upvotes: 2
Reputation: 664
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;
}```
Upvotes: 2
Reputation: 484
It might be already quite late for this answer, but anyway here is how I solved:
tsconfig
file (this could be a whole new thread)express
express
directory create a file and name it index.d.ts
(MUST BE EXACTLY LIKE THAT)declare module 'express' {
export interface Request {
property?: string;
}
}
Upvotes: 1
Reputation: 4721
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
Upvotes: 14
Reputation: 854
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.
Upvotes: 29
Reputation: 3654
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 =
});
Upvotes: 119
Reputation: 339
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
Upvotes: 18
Reputation: 759
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.
Upvotes: 5
Reputation: 12707
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
}
}
Upvotes: 148
Reputation: 959
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 {
...
}
Upvotes: 17
Reputation: 3767
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)
})
Upvotes: 350