I'm retrieving the user object from the database and setting it on the express-session:
export const postLogin = async (
request: Request,
response: Response,
next: NextFunction
): Promise<void> => {
try {
request.session.user = await UserModel.findById('6127bd9d204a47128947a07d').orFail().exec()
response.redirect('/')
} catch (error) {
next(error)
}
}
Then I call the Mongoose method populate()
on the user object to get the cart associated with it:
export const getCart = async (
request: Request,
response: Response,
next: NextFunction
): Promise<void> => {
try {
const userWithCartProducts = await request.session.user
.populate('cart.items.productId')
.execPopulate()
} catch (error) {
next(error)
}
}
But here I'm getting an error: TypeError: request.session.user.populate is not a function
I have defined the custom user type on express-session like following:
declare module 'express-session' {
interface SessionData {
user?: DocumentType<User>
}
}
As you can see in above definition of user
, I'm using DocumentType<User>
because I'm typing my models using Typegoose. I'm not sure if this is how it's done for Typegoose.
What am I doing wrong? Any input would be much appreciated.
The problem is that the user
object in the session
object was fetched by the MongoDBStore
. The MongoDBStore
is not aware of the User
model that is defined in Typegoose, so, when it fetches the data from the session database, it only fetches the raw data, not the methods defined inside the Typegoose model.
So, to fix this, when a new request comes in, after initializing the express-session, in a middleware, we need to fetch the user once from the database and put in on the request
object like following:
app.use(initializeUser)
export const initializeUser = async (request: Request, response: Response, next: NextFunction): Promise<void> => {
try {
request.user = await User.findById(request.session.user._id).orFail().exec()
next()
} catch (error) {
next(error)
}
}
For Typegoose, define the User
model on the Request
as following:
declare global {
declare namespace Express {
export interface Request {
user?: DocumentType<User>
}
}
}
As mentioned in the question, in the login
route, even though we are storing the user
object in the session
, the request dies down there and the session (along with the user
object) gets saved to the database. But the next time the request
comes in, the user
object available in the session
is retrieved by the MongoDBStore
which has no knowledge of the User
Model and the methods defined by the Typegoose. So, this is how we fix that problem. The same solution is also applied in Javascript with Mongoose.