Typescript express typing on Request object - node.js

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.

Related

How to add custom properties on the Request object in Express + TypeScript?

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.

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.

Why Node js joi validation is not working as expected?

I have implemented joi validation for my project but it's not working for any single field. whatever you pass it's get stored in database it doesn't validate any field even though i did a code for validation
Here is a code for validation
import * as Joi from "joi";
import { Request, Response, NextFunction } from 'express';
import { StatusCodes } from 'http-status-codes';
import { sendError } from "../responseHelper";
import { validationOptions } from "./_index";
export class CountryValidator {
public async createCountryValidator(req: Request, res: Response, next: NextFunction) {
try {
const schema = Joi.object({
id: Joi.number().required(),
name: Joi.string().required(),
code: Joi.string().required(),
status: Joi.number().valid(0, 1).required(),
});
schema.validate(req.body, validationOptions);
next();
} catch (error) {
sendError(res, error, error.code, StatusCodes.INTERNAL_SERVER_ERROR);
}
}
}
And this is my route path
adminRoute.route('/country/create')
.post(countryValidator.createCountryValidator, countryController.createCountry);
And on this path I'm posting below data is which totally wrong as per validation but still it accepts all the data and not throwing any validation error
{
"name":"BR1Z",
"code":100,
"status":"1"
}
Can any one help me to resolve this issue ?
schema.validate returns object with error filed (instead of throwing error).
...
const joiRes = schema.validate(req.body, validationOptions);
if(joiRes.error){
sendError(res, error, error.code, StatusCodes.INTERNAL_SERVER_ERROR);
}
...
see: https://joi.dev/api/?v=17.4.1

Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'

Please help, I am getting this error
src/app/middlewares/authentication.ts:16:17 - error TS2339: Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
16 req.user = user;
I have created the .d.ts file and also included it in tsconfig file. Still I am not able to run this code
Please find attached screenshots
Create a types folder in your src directory
Create a folder within the types folder with the name of the package you intend to extend. (In this case express).
Create an index.d.ts file in that folder
src/
- types/
- express/
- index.d.ts
add this code to the index file
import express from "express";
declare global {
namespace Express {
interface Request {
user?: Record<string,any>
}
}
}
remember to update your tsconfig.json file
{
"compilerOptions": {
"typeRoots" : ["./src/types", "./node_modules/#types"]
}
}
This should work
I was stuck on the same problem earlier. Here is how I solved it.
I created a separate directory called #types in my project for declaration merging to work.
Next I created a file in it called index.d.ts with following content. Please pay attention that we need to declare our own request within global. Also, importing express is important as well.
import * as express from "express"
declare global {
namespace Express {
interface Request {
user? : Record<string,any>
}
}
}
I added the following line under compilerOptions in my tsconfig.json.
"compilerOptions": {
...other settings,
"typeRoots": ["#types", "node_modules/#types"],
...other settings
}
And that's it. It should works with these changes.
Another way:
import { NextFunction, Request, Response } from 'express';
interface IDecode {
address: string,
role: string,
iat: number,
exp: number
};
interface RequestWithUserRole extends Request {
user?: IDecode,
}
const parseToken = (secret: string) =>
async (req: RequestWithUserRole, res: Response, next: NextFunction) => {
try{
const token = req.headers?.authorization?.split(' ')[1];
// console.log(req.headers?.authorization);
if (!token) {
return res.status(403).json({message: 'Token not found'})
}
const decodedData = <IDecode> jwt.verify(token, secret);
req.user = decodedData;
// console.log(decodedData);
return next();
}
catch(e) {
return res.status(500).json({e})
}
};
A quick walkaround if you are doing a quick prototype or following a tutorial
(req as any).user
NB: Do not use this in a real production app, because it would make your code dirty and it's not scalable.

Resources