2

I am using the request handler interface to design my controllers. Here is an example

import { RequestHandler } from "express";

//#region GetLoggedInUserInformation
type GetLoggedInUserInformationControllerParams = {};
type GetLoggedInUserInformationControllerBodyRequest = {};
type GetLoggedInUserInformationControllerResponse = {
  age: number;
  weightKg: number;
  heightCm: number;
  gender: string;
};
type GetLoggedInUserInformationControllerLinkQuery = {};

export const GetLoggedInUserInformationController: RequestHandler<
  GetLoggedInUserInformationControllerParams,
  GetLoggedInUserInformationControllerResponse,
  GetLoggedInUserInformationControllerBodyRequest,
  GetLoggedInUserInformationControllerLinkQuery
> = async (req, res, next) => {
  try {
    // Controller logic
  } catch (e) {
    next(e);
  }
};
//#endregion

The problem is that i am passing custom variables to the request during the middleware logic. Typescript is always throwing an error when i try to read a variable in the req object. Even after declaring global namespace like below :-

declare namespace Express {
  interface Request {
    userId : number
  }
}

Here is the error when i try to read that variable

Property 'userId' does not exist on type 'Request<GetLoggedInUserInformationControllerParams, GetLoggedInUserInformationControllerResponse, GetLoggedInUserInformationControllerBodyRequest, GetLoggedInUserInformationControllerLinkQuery, Record<...>>'.ts(2339)

I tried to add custom header interface like this :-

export interface ExtendedHeader extends Request {
  userId: number;
}

And modify the controller to be like this

export const getLoggedInUserInformationController: RequestHandler<
  GetLoggedInUserInformationControllerParams,
  GetLoggedInUserInformationControllerResponse,
  GetLoggedInUserInformationControllerBodyRequest,
  GetLoggedInUserInformationControllerLinkQuery
> = async (req: ExtendedHeader, res, next) => {
  try {
    const userInformation = await prisma.user.findUnique({
      where: { id: req.userId },
    });

    res.json({ result: true });
  } catch (e) {
    next(e);
  }
};

I get an error on the function initiating line with this info

const getLoggedInUserInformationController: RequestHandler<GetLoggedInUserInformationControllerParams, GetLoggedInUserInformationControllerResponse, GetLoggedInUserInformationControllerBodyRequest, GetLoggedInUserInformationControllerLinkQuery, Record<...>>
Type '(req: ExtendedHeader, res: Response<GetLoggedInUserInformationControllerResponse, Record<string, any>, number>, next: NextFunction) => Promise<...>' is not assignable to type 'RequestHandler<GetLoggedInUserInformationControllerParams, GetLoggedInUserInformationControllerResponse, GetLoggedInUserInformationControllerBodyRequest, GetLoggedInUserInformationControllerLinkQuery, Record<...>>'.
  Types of parameters 'req' and 'req' are incompatible.
    Property 'userId' is missing in type 'Request<GetLoggedInUserInformationControllerParams, GetLoggedInUserInformationControllerResponse, GetLoggedInUserInformationControllerBodyRequest, GetLoggedInUserInformationControllerLinkQuery, Record<...>>' but required in type 'ExtendedHeader'.ts(2322)
types.ts(4, 3): 'userId' is declared here.

I found the only solution for this is to remove the RequestHandler interface and use the Request Response NextFunction interfaces which coming from express. But there is a lot of controllers that have been designed in that why in our application and i wan't to follow the same procedure with the rest for code readability.

So any idea how i can use RequestHandler interface with extended Request ?

Phil
  • 157,677
  • 23
  • 242
  • 245
Ahmed Ahmed Sayed
  • 195
  • 1
  • 4
  • 12
  • Does this answer your question? [Extend Express Request object using Typescript](https://stackoverflow.com/questions/37377731/extend-express-request-object-using-typescript). Note that the accepted answer is obsolete but the other answers should help – Phil May 03 '22 at 00:45
  • @Phil Thanks Phill, Unfortunately no, The user who asked that question is not using RequestHandler interface, Thus the answers doesn't apply to me. Even though i have tested the mentioned answers. Still throwing the error – Ahmed Ahmed Sayed May 03 '22 at 00:50
  • 1
    Shouldn't matter. The `RequestHandler` implicitly uses `Request` which if you augment as shown in [this answer](https://stackoverflow.com/a/47448486/283366), should have the properties you want – Phil May 03 '22 at 00:53
  • I tested this locally and it works fine for me. I have the `declare global { ... }` from the linked duplicate and `export const getLoggedInUserInformationController: RequestHandler<{}, GetLoggedInUserInformationControllerResponse> = (req, res) => { ... }` – Phil May 03 '22 at 01:06
  • @Phill I have tested it now. It worked only by declaring the global scope in the app.ts file (the main file which contains all the app.use() functions). When i try to put it in the global.d.ts i get this error ```Augmentations for the global scope can only be directly nested in external modules or ambient module declarations``` I tried to add ```export {}``` but it's still not working. So the only method to work is to put it inside the app.ts ? – Ahmed Ahmed Sayed May 03 '22 at 01:11

0 Answers0