How to add Typescript definitions to Express req & res - node.js

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

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.

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.

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.

What is the proper type for a request handler of a firebase HttpsFunction?

I need to split my functions into multiple files.
This is my index.ts
export const helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
I need it to be something like:
import helloWorldHandler from "./handlers/helloWorldHandler"
export const helloWorld = functions.https.onRequest(helloWorldHandler);
So what should I type the helloWorldHandler request handler?
const helloWorldHandler : ??? = async (req,res) => {
const result = await someApi();
functions.logger.info("Hello logs!", {structuredData: true});
res.send("Hello from Firebase!");
};
export default helloWorldHandler;
I tried:
import * as functions from "firebase-functions";
const helloWorldHandler : functions.HttpsFunction = async (req,res) => { ... };
But I'm getting this error:
Type '(req: Request, res: Response) => Promise<Response>' is not assignable to type 'HttpsFunction'.
Property '__trigger' is missing in type '(req: Request, res: Response) => Promise<Response>' but required in type 'TriggerAnnotated'.
The onRequest() method, the one that should take the handler as a parameter, does not seem to give it a proper type name, rather than a function signature. Do I need to create an alias for that?
The type functions.HttpsFunction is the return type of functions.https.onRequest(), and not the argument to it. A function of this type is exported by your code and defines what needs to be deployed by the Firebase CLI (the region, memory size and so on are stored in the __trigger property).
As you want the type of the first argument to functions.https.onRequest(), you are instead looking for the type:
type HttpsOnRequestHandler = (req: functions.https.Request, resp: functions.Response<any>) => void | Promise<void>
But rather than hard-code this, you can extract it from the Firebase Functions library using either:
import * as functions from "firebase-functions";
type HttpsOnRequestHandler = Parameters<typeof functions.https.onRequest>[0];
or
import { https } from "firebase-functions";
type HttpsOnRequestHandler = Parameters<typeof https.onRequest>[0]
Note: If your code doesn't use the firebase-functions library itself, you can tell TypeScript that you only want its types using import type * as functions from "firebase-functions"; and import type { https } from "firebase-functions"; as appropriate; this removes the import from the compiled JavaScript as its not needed for running the code.
Here is what I've come up with. Still interested in knowing if is there an out-of-the-box type alias for this.
index.ts
import * as functions from "firebase-functions";
import helloWorldHandler from "./handlers/helloWorldHandler"
type Req = functions.https.Request
type Res = functions.Response
// WILL USE THIS EXPORTED TYPE
export type RequestHandler = (req: Req, res: Res) => void | Promise<void>
export const helloWorld = functions.https.onRequest(helloWorldHandler);
helloWorldHandler .ts
import { RequestHandler } from "../index";
const helloWorldHandler : RequestHandler = async (req,res) => { ... };
I'd suggest simply using the provided HttpsFunction type:
import * as functions from "firebase-functions";
import type { HttpsFunction } from "firebase-functions";
export const foo: HttpsFunction = functions.https.onRequest((req, res) => {
// the types of req and res are known here...
res.send("HELO");
});
I managed to run the new Firebase Functions v2 in TypeScript with
// import from a specific subpackage
import {onRequest} from "firebase-functions/v2/https";
export default onRequest((request, response) => {
response.send("Hello from Firebase!");
});

correlationId dosn't exist in node.js

I am trying to add correlationId using express-correlation-id. I am exactly following the page: https://www.npmjs.com/package/express-correlation-id. I've imported the express-correlation-id pkg and have found it in the package.json and node modules.
But when i tried to get req.correlationId(), it always said:
Property 'correlationId' dosn't exist on type 'Request<ParamsDictionary, any, any, ParsedQs>'.
I'm using TypeScript and Express, this is what the code looks like:
import correlator = require("express-correlation-id");
app.use(correlator());
app.get('/', (req, res) => {
req.correlationId(); // where the error occurs
});
Since this package is written in js language, there are no type definition files for typescript, so you need to extend the Express.Request interface by yourself, add the correlationId method to the interface.
Package versions:
"typescript": "^3.9.7"
"express-correlation-id": "^1.3.1",
"express": "^4.17.1",
E.g.
server.ts:
import express from 'express';
import correlator = require('express-correlation-id');
declare global {
namespace Express {
export interface Request {
correlationId(): string;
}
}
}
const app = express();
app.use(correlator());
app.get('/', (req, res) => {
req.correlationId();
});
This is because the property correlationId doesn't exist in the typing of req.
So, if you see the typing of req is:
export interface Request<P = ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = ParsedQs> extends http.IncomingMessage, Express.Request {
So, you must create a new Request type with the new functions that you're going to use. For your case:
import express from "express";
import correlator = require("express-correlation-id");
import { ParamsDictionary, Request } from "express-serve-static-core";
import { ParsedQs } from "qs";
const app = express();
interface CustomReq<P = ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = ParsedQs> extends Request<P, ResBody, ReqBody, ReqQuery> {
// extended options
correlationId: () => any;
}
app.use(correlator());
app.get('/', (req: CustomReq, res) => {
req.correlationId(); // OK
});
As you see in the code above, my new Request is CustomReq and I use the new type for the req.

Resources