I have to keep doing stuff like
if (!req.user) return res.status(401).send()
The approach the comes into mind would be an express middleware for that. But even tho I can prevent non-logged users from reaching the route I can't think of a way to type express req correctly.
If I override the "req" param the router complains because the user key in "Express.User" is an optional parameter.
I don't believe changing the global override so "user" is required is a good option since "user" should only be required after the middleware validation. How can I achieve this?
Below some piece of useful code to understand the context.
Global Express Override
declare global {
namespace Express {
interface User extends TUser {
_id: ObjectId
}
}
}
What I want to achieve
// Router
router.post('/', sessionMiddleware, controller)
// Middleware
const sessionMiddleware = (req: Request, res: Response, next: NextFunction) => {
if (!req.user) return res.status(401).send()
next()
}
// Controller
const controller = (req: RequestWithRequiredUser, res: Response) => {
// user here can't possibly be typed undefined
const user = req.user
...
}
What I actually have to do everytime:
const controller = (req: Request, res: Response) => {
if (!req.user) return res.status(401).send()
...doSomethingElse
}
CodePudding user response:
I don't if better solution with middlewares are available but this is a workaround I found to avoid repeating logic.
buildSessionController.ts
/** Make given keys required */
export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }
type TController<T extends Request> = (req: WithRequired<T, 'user'>, res: Response) => unknown
export const buildSessionController =
<T extends Request>(controller: TController<T>) =>
(req: Request, res: Response) => {
if (!req.user) return res.status(401).send()
// This is needed as a type workaround because a type mismatch happens otherwise
// Using Omit instead of WithRequired above doesn't fix it
const request = req as WithRequired<T, 'user'>
return controller(request, res)
}
someController.ts
export const expressController =
buildSessionController((req, res) => {
// user will never be undefined here
const { user } = req
return res.json(user)
})