How to add custom properties on the Request object in Express + TypeScript? - node.js

I am trying to add a user object as a custom property in the Request object of Express, but I got the following error:
Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'
This is my code in a middleware function:
// Authenticate person/user through the database.
const person = new Person(username, password);
const user = await authenticate(person); // ORM => read DB
if (!user) {
return res
.status(401)
.json({ message: "Invalid Authentication Credentials" });
}
// attach user to request object
req.user = user; // <= HERE is my problem
next();
How I can add this custom property to the request?

I think the standard way to do this is by extending the Response interface exported by Express and declaring your Data as being part of the Locals generic type.
In #types/express/index.d.ts # line 127:
export interface Response<ResBody = any, Locals extends Record<string, any> = Record<string, any>>
extends core.Response<ResBody, Locals> {}
You can therefore create a Type that will be used in lieu of the default value for the Locals generic like so:
import type { Response, Request, NextFunction } from 'express';
import type { User } from './models'; // Or wherever it is, obviously.
type MyLocals = { user?: User; };
type MyResponse = Response<any, MyLocals>
// Using the `MyResponse` type is as simple as setting the type of `res` to be `MyResponse`, e.g.:
async function doSomeWork (req: Request, res: MyResponse, next: NextFunction): Promise<void> {
console.log(res.locals.user); // undefined | User
}
There are some other ways to achieve this, as well, but this is what I have always done when using Typescript + Express.

Related

Typescript express typing on Request object

I seem to have an issue with Typescript typings on my Express Request object. The project for now exists out of 2 sub-projects (user-service and a common project which includes reusable Errors and Middlewares)
The common folder is installed as a dependency in the user-service like:
"#myPackage/common": "file:../common",
In there I have a current-user middleware:
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
interface UserPayload {
id: string;
email: string;
}
declare global {
namespace Express {
interface Request {
currentUser?: UserPayload;
}
}
}
const currentUser = (
req: Request,
res: Response,
next: NextFunction,
) => {
if (!req.session?.jwt) {
return next();
}
try {
const payload = jwt.verify(
req.session.jwt,
process.env.JWT_KEY!,
) as UserPayload;
req.currentUser = payload;
} catch (err) {
console.error(err);
}
return next();
};
export default currentUser;
with a declared global for the currentUser property on the Request object.
In my user-service project I have the following route
import express, { Request, Response } from 'express';
import { Middlewares } from '#myPackage/common';
const router = express.Router();
router.get('/api/users/currentuser', Middlewares.currentUser, (
req: Request,
res: Response,
) => {
res.send({ currentUser: req.currentUser || null });
});
export default router;
On req.currentUser I get the following error message:
Property 'currentUser' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
Shouldn't the package typings automatically be taken over in the code in which you import it? I hope I made myself clear on what the problem is :)
I've also always had trouble declaring a global namespace to attach types to express's request object. Found myself a solution using "declare module" instead of "declare global". So instead of
declare global {
namespace Express {
interface Request {
currentUser?: UserPayload;
}
}
}
maybe give the following approach a try:
declare module "express-serve-static-core" {
interface Request {
currentUser?: UserPayload;
}
}
Normally your currentUser property should also be available in other files with this approach, but you can of course export the manipulated Request interface if not.
Note that in most cases you will need to reference the "express-serve-static-core" module as this is where the Request interface is declared.

Accessing middleware injected data in Typescript

Here's a high level view of a controller in Typescript-Node :
As I'm storing details of user in product model, I have used a middleware to check if user if logged in before accessing the endpoint and also injecting user info to the req which can be further used in different controllers
exports.addProduct = async (req: Request, res: Response, next: NextFunction) => {
// images:
try {
// logic to handle data from req.body
// getting this user id from middleware isLoggedIn
// injecting user id into request in the isLoggedIn middleware
req.body.user = req.user._id;
const product = await Product.create(req.body);
return res.status(200).json({
success: true,
product,
});
} catch (error) {
logger.error(error);
}
};
Getting error : Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' , on the line
req.body.user = req.user._id;
isLoggedIn is typical function to check Bearer token or header or cookies and then inject user info to the request
It worked Perfectly in Javascript, now trying same in Typescript as a part to learn Typescipt
There are two ways to achieve this:
Extending express Request locally
Extending express Request globally
Using the local way require to write lots of redundent code and that's why
the global way is much better. it can be done by creating file as follows:
index.d.ts
import { User } from "../../models/user";
// to make the file a module and avoid the TypeScript error
export {};
declare global {
namespace Express {
export interface Request {
user: User;
}
}
}
Then add this config to tsconfig.json file
"typeRoots": [
"src/#types",
"./node_modules/#types",
],
Then Request object will recognize user and user can be injected from any middleware to be used in any controller.
The problem is that according to the typing of req, there is no property named user. TypeScript is notifying you that req.user should be undefined, according to the available typings. There are some possible solutions to fix your problem.
You could explicitly type the variable as any. This is considered to be bad practice sometimes, because in general you should try to type everything correctly (nevertheless: it works).
// Option 1: Explicitly declare variable as any
req.body.user = (req as any).user._id;
You could also check if req.user is defined, like this:
// Option 2: Check req.user manually
if (req.user) req.body.user = req.user._id;
else throw new Error("Some Error");
You could also type the req correctly, according to the API specifications of your middleware. This is usually a lot of work if done manually. Some modules ship with correct TypeScript-typings already.
Maybe you want to also look into this question since it is very similar to your question.
first create a folder call types it should be at the root of your project
then at yow tsconfig.json in the compilerOptions section add a paths prop
{
"compilerOptions": {
...
"paths": {
"express": [
"./types/express/index.d.ts"
],
}
}
then at the types dir add a new dir call express inside add an index.d.ts go ahead a copy them express definitions
// Type definitions for Express 4.17
// Project: http://expressjs.com
// Definitions by: Boris Yankov <https://github.com/borisyankov>
// China Medical University Hospital <https://github.com/CMUH>
// Puneet Arora <https://github.com/puneetar>
// Dylan Frankland <https://github.com/dfrankland>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/* =================== USAGE ===================
import express = require("express");
var app = express();
=============================================== */
/// <reference types="express-serve-static-core" />
/// <reference types="serve-static" />
import * as bodyParser from 'body-parser';
import * as serveStatic from 'serve-static';
import * as core from 'express-serve-static-core';
import * as qs from 'qs';
/**
* Creates an Express application. The express() function is a top-level function exported by the express module.
*/
declare function e (): core.Express;
declare namespace e {
/**
* This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on body-parser.
* #since 4.16.0
*/
var json: typeof bodyParser.json;
/**
* This is a built-in middleware function in Express. It parses incoming requests with Buffer payloads and is based on body-parser.
* #since 4.17.0
*/
var raw: typeof bodyParser.raw;
/**
* This is a built-in middleware function in Express. It parses incoming requests with text payloads and is based on body-parser.
* #since 4.17.0
*/
var text: typeof bodyParser.text;
/**
* These are the exposed prototypes.
*/
var application: Application;
var request: Request;
var response: Response;
/**
* This is a built-in middleware function in Express. It serves static files and is based on serve-static.
*/
var static: serveStatic.RequestHandlerConstructor<Response>;
/**
* This is a built-in middleware function in Express. It parses incoming requests with urlencoded payloads and is based on body-parser.
* #since 4.16.0
*/
var urlencoded: typeof bodyParser.urlencoded;
/**
* This is a built-in middleware function in Express. It parses incoming request query parameters.
*/
export function query (options: qs.IParseOptions | typeof qs.parse): Handler;
export function Router (options?: RouterOptions): core.Router;
interface RouterOptions {
/**
* Enable case sensitivity.
*/
caseSensitive?: boolean | undefined;
/**
* Preserve the req.params values from the parent router.
* If the parent and the child have conflicting param names, the child’s value take precedence.
*
* #default false
* #since 4.5.0
*/
mergeParams?: boolean | undefined;
/**
* Enable strict routing.
*/
strict?: boolean | undefined;
}
interface SessionData {
userIp: string;
ipDetails: any;
publicKey: string;
session: string;
iv: string;
decrypted: any;
}
interface Application extends core.Application { }
interface CookieOptions extends core.CookieOptions { }
interface Errback extends core.Errback { }
interface ErrorRequestHandler<
P = core.ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = core.Query,
Locals extends Record<string, any> = Record<string, any>
> extends core.ErrorRequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> { }
interface Express extends core.Express { }
interface Handler extends core.Handler { }
interface IRoute extends core.IRoute { }
interface IRouter extends core.IRouter { }
interface IRouterHandler<T> extends core.IRouterHandler<T> { }
interface IRouterMatcher<T> extends core.IRouterMatcher<T> { }
interface MediaType extends core.MediaType { }
interface NextFunction extends core.NextFunction { }
interface Request<
P = core.ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = core.Query,
Locals extends Record<string, any> = Record<string, any>
> extends core.Request<P, ResBody, ReqBody, ReqQuery, Locals> { }
interface RequestHandler<
P = core.ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = core.Query,
Locals extends Record<string, SessionData> = Record<string, SessionData>
> extends core.RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> { }
interface RequestParamHandler extends core.RequestParamHandler { }
export interface Response<ResBody = any, Locals extends Record<string, SessionData> = Record<string, SessionData>>
extends core.Response<ResBody, Locals> { }
interface Router extends core.Router { }
interface Send extends core.Send { }
}
export = e;
if you notice from the above I added an interface call SessionData if you look almost at the end I set Locals to be equals to it. at the Response
now at yow endPoint you can apply it like this
import type e from "express";
export const endPoint: e.RequestHandler = (req, res, next) => {
//code
};
you can go beong that you can also add them params if there are any, the res body, the req body and stuff
const endPoint: e.RequestHandler<YowParamsObj,YowResBodyObj,YowReqBodyObj,ThemQueryParamsObj> = (req, res, next) => {
//code
};

Error no overload matches this call( Typescript + Express + JWT)

I'm learning typescript and I'm trying to build a middleware function that would use jsonwebtoken to manage the rights of the users.
I've first setup a verify token middleware function and I've created an interface that extends the Request interface of Express in this function. By doing this I'm able to attach a users property on the request object that I can use on the jwt.verify callback function. Everything works fine for this part. I don't get any error
But then, when I add the verify function to my router I get the following
error code in the router ts file:
No overload matches this call.
The last overload gave the following error.
Argument of type '(req: IGetUserAuthInfoRequest, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined' is not assignable to parameter of type 'RequestHandlerParams<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
When I remove the callback function in the jwt.verify function and I set the req parameter type to Request, the error disappears but it's not the expected behaviour since I'm not able to access anymore the user parameter of the request object.
Here is the code for my verify function:
import { RequestHandler, Request, NextFunction,Response } from 'express';
import * as jwt from 'jsonwebtoken';
interface IGetUserAuthInfoRequest extends Request {
users: jwt.JwtPayload | undefined // or any other type
}
// Check if jsonwebtoken is valid
const verify = (req:IGetUserAuthInfoRequest, res:Response, next:NextFunction) =>{
const authHeader = req.headers.token
if (authHeader && typeof authHeader === 'string') {
const token = authHeader.split(" ")[1]
jwt.verify(token, `${process.env.ACCESS_TOKEN_SECRET_KEY}`, (err, users)=> {
if (err) {res.status(403).json("Token is not valid")}
req.users = users
next()
})
} else {
return res.status(401).json("Not authenticated")
}
}
Here is my code for my router where I get the error:
import { Router } from "express";
import verify from "../verifyToken";
import { updateUser } from "../controllers/userController";
const router = Router();
router.put("/:id", verify,updateUser)
export default router
Finally, I've tried to update the req parameter type in the verify function by setting it to Request like the code below:
import { RequestHandler, Request, NextFunction,Response } from 'express';
import * as jwt from 'jsonwebtoken';
// Check if jsonwebtoken is valid
const verify = (req:Request, res:Response, next:NextFunction) =>{
const authHeader = req.headers.token
if (authHeader && typeof authHeader === 'string') {
const token = authHeader.split(" ")[1]
jwt.verify(token, `${process.env.ACCESS_TOKEN_SECRET_KEY}`, (err, users)=> {
if (err) {res.status(403).json("Token is not valid")}
req.users = users
next()
})
} else {
return res.status(401).json("Not authenticated")
}
}
export default verify
When I do this change I get another error on the users property saying:
Property 'users' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>
I'm really confuse and I don't understand what I'm doing wrong in my logic.

Expression of type 'string' can't be used to index type 'Request<ParamsDictionary, any, any, Query>'

I am trying to create a middleware for validating the input data in request.
export function validator(schema: Joi.ObjectSchema, key: string) {
return function (req: Request, res: Response, next: NextFunction): void {
try {
Joi.assert(req[key], schema);
next();
} catch (error) {
console.log(error);
throw new Error(error);
}
};
}
But the req[key] is throwing following error:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Request'.
No index signature with a parameter of type 'string' was found on type 'Request'.
The other solutions that I found to problems similar to this include the interface in their own code. I don't understand why this error is coming and how to solve this?
Also the following code is from the express's type definition file.
interface Request<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = core.Query> extends core.Request<P, ResBody, ReqBody, ReqQuery> { }
What does the above definition mean?
Changing key: string in the definition of validator to key: keyof Request worked.

How to add Typescript definitions to Express req & res

I have a set of controller functions for my REST API and I'm getting lots of the following
error TS7006: Parameter 'req' implicitly has an 'any' type.
Likewise for res. I've been playing around with typeings etc. but with no success. For example the Request type parameter below does NOT work.
Here is an example of the controller files. The reference path is correct.
/// <reference path="../../../typings/tsd.d.ts" />
/* globals require */
"use strict";
exports.test = (req : Request, res) => {
I tried adding import * as express from "express"; into the file - I don't need it normally as these functions are exported and use by index.js which actually implements the routing.
And this is tsd.d.ts
/// <reference path="requirejs/require.d.ts" />
/// <reference path="express/express.d.ts" />
/// <reference path="mime/mime.d.ts" />
/// <reference path="node/node.d.ts" />
/// <reference path="serve-static/serve-static.d.ts" />
/// <reference path="bluebird/bluebird.d.ts" />
/// <reference path="mongoose/mongoose.d.ts" />
You can use ES6 style named imports to import only the interfaces you need, rather than import * as express from 'express' which would include express itself.
First, make sure you have installed the type definitions for express (npm install -D #types/express).
Example:
// middleware/authCheck.ts
import { Request, Response, NextFunction } from 'express';
export const authCheckMiddleware = (req: Request, res: Response, next: NextFunction) => {
...
};
// server.ts
import { authCheckMiddleware } from './middleware/authCheck';
app.use('/api', authCheckMiddleware);
Currently using TypeScript 2.3.4 and #types/express 4.0.36.
It can be daunting to type the arguments every time you need to write middleware functions so you can just type the whole function directly too.
npm i #types/express --save-dev ("#types/express": "^4.17.0")
After installing typings..
// This can be shortened..
import { Request, Response, NextFunction } from 'express';
export const myMiddleware = (req: Request, res: Response, next: NextFunction) => {
...
};
// to this..
import { RequestHandler } from 'express';
export const myMiddleware: RequestHandler = (req, res, next) => {
...
};
// or in case it handles the error object
import { ErrorRequestHandler } from 'express';
export const myMiddleware: ErrorRequestHandler = (err, req, res, next) => {
...
};
What I've found is that you can leverage TypeScript generics very effectively to create a wrapper around the Express Request type.
You can declare something that looks similar to this in an interfaces file/folder:
import { NextFunction, Request, Response } from 'express';
type TypedRequest<
ReqBody = Record<string, unknown>,
QueryString = Record<string, unknown>
> = Request<
Record<string, unknown>,
Record<string, unknown>,
Partial<ReqBody>,
Partial<QueryString>
>;
export type ExpressMiddleware<
ReqBody = Record<string, unknown>,
Res = Record<string, unknown>,
QueryString = Record<string, unknown>
> = (
req: TypedRequest<ReqBody, QueryString>,
res: Response<Res>,
next: NextFunction
) => Promise<void> | void;
TypedRequest is effectively a wrapper around Express' Request interface, and populates it with the generics that you pass it, but are also optional (note Record<string, unknown>. It then also applies a Partial around each of the generics (you probably want to make this a DeepPartial instead)
ExpressMiddleware takes in 3 optional generics ReqBody Res and QueryString. These are used to construct a function signature that resembles what middlewares/controllers should look like.
The above then allows you to strongly type & consume as follows:
import { ExpressMiddleware } from '../interfaces/ExpressMiddleware';
type Req = { email: string; password: string };
type Res = { message: string };
export const signupUser: ExpressMiddleware<Req, Res> = async (req, res) => {
/* strongly typed `req.body`. yay autocomplete 🎉 */
res.json({ message: 'you have signed up' }) // strongly typed response obj
};
I hope this helps someone. It's made a massive difference to my Express experience.
The best way to do this is like so.
// create some shared types in your project
import { Request, Response, NextFunction } from 'express';
export type MiddlewareFn = (req: Request, res: Response, next: NextFunction) => void;
// then use the above types:
import {MiddlewareFn} from './my-types.d.ts'
router.get('/foo', <MiddlewareFn>function (req, res, next) {
// ....
});
Rather than installing types(#types/express) you should also define request parameters. Since every parameter is string, interface should base on dictionary.
Here is an inline route handler:
interface GetParams {
[key: string]: string
paramName: string
}
router.get<GetParams>('/:paramName', (req, res) => {
res.send('Parameter is ' + req.params.paramName)
})
Use:
req: Express.Request
res: Express.Response

Resources