Erfan Asbari
Erfan Asbari

Reputation: 53

typescript: req.user is possibly 'undefined' - express and passport js

I have a Node.js TypeScript authentication system that works using passport.

My problem is that when I use req.user in a route I get this error on req.user: Object is possibly 'undefined'.

This is a normal behavior of Typescript but I am using a middleware to protect routes that I want to use req.user in them and this way req.user cannot be undefined.

This is where I extend Express.User Type:

import { users } from "@prisma/client";

declare global {
    namespace Express {
        interface User extends users {}
    }
}

This is the middleware that I'm using to protect routes for logged in users:

export function checkIsAuthenticated(req: Request, res: Response, next: NextFunction) {
    if (req.isAuthenticated()) {
        if (!req.user) req.logOut();
        else return next();
    }
    res.status(400).json({
        errors: [{ message: "no user logged in" }],
    });
}

And this is the route for getting the user info:

export function userRoute(req: Request, res: Response) { // defining the route
    res.json({
        id: req.user.id,               // I'm getting the error in these 4 lines
        username: req.user.username,   //
        email: req.user.email,         //
        role: req.user.role,           //
    });
}

router.get("/user", checkIsAuthenticated, userRoute); // using the route

I don't want to check if user is defined because I don't want to do it in every route and that's not a good practice. That's why middleware is for.

I'm not good at Typescript so I need some help to fix it.

Upvotes: 4

Views: 4272

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075527

I don't want to check if user is defined because I don't want to do it in every route and that's not a good practice.

I'd quibble that there's nothing wrong with checking the request for a user and giving yourself a nice useful error message if you've accidentally used one of these handlers on a route that you forgot to put the authentication on. That would look like this:

type RequestWithUser = Request & {user: typeOfUserObject};
function assertHasUser(req: Request): asserts req is RequestWithUser {
    if (!( "user" in req)) {
        throw new Error("Request object without user found unexpectedly");
    }
}

Then your handler for those routes:

export function userRoute(req: Request, res: Response) {
    assertHasUser(req);
    // ...you can use `req.user` here...
});

Playground example

(You can just use asserts req is Request & {user: typeOfUserObject} if you don't want a RequestWithUser type, but it's often useful to have an alias.)

Upvotes: 3

Alexander Gordon
Alexander Gordon

Reputation: 139

export function userRoute(req: Request, res: Response) { // defining the route
    res.json({
        id: req.user!.id,               // you tell typescript that req.user for sure not. null
        username: req.user!.username,   //
        email: req.user!.email,         //
        role: req.user!.role,           //
    });
}

router.get("/user", checkIsAuthenticated, userRoute);

Upvotes: -1

Related Questions