cabc
cabc

Reputation: 35

How to correctly create a custom Express.js request interface with Typescript?

I'm trying to create a custom Express request interface for my API. I do this by creating a custom interface called AuthRequest which extends Request from Express. How ever when trying to import my interface and define req to use that interface I in my middleware function I get the following error from Typescript:

No overload matches this call.
  The last overload gave the following error.
    Argument of type '(req: AuthRequest, res: Response, next: NextFunction) => void' is not assignable to parameter of type 'PathParams'.
      Type '(req: AuthRequest, res: Response<any, Record<string, any>>, next: NextFunction) => void' is missing the following properties from type '(string | RegExp)[]': pop, push, concat, join, and 28 more.ts(2769)
index.d.ts(165, 5): The last overload is declared here.

Project structure:

.
+-- src
|   +-- index.ts
|   +-- interface.ts
+-- package.json
+-- tsconfig.json

index.ts

import express, { Response, NextFunction } from "express";
import { AuthRequest } from "./interface";

const app = express();
const port = 8080;

app.use((req: AuthRequest, res: Response, next: NextFunction) => {
  next();
});

app.listen(port, () => {
  console.log(`App started on port ${port}`);
});

interface.ts

import { Request } from "express";

interface User {
  id: number;
}
export interface AuthRequest extends Request {
  user: User;
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
  },
  "include": [
    "**/*.ts"
  ],
  "exclude": [
    "node_modules",
    "dist/"
  ]
}

package.json

{
  "name": "ts-express-server",
  "version": "1.0.0",
  "description": "Typescript Express Server",
  "main": "src/index.ts",
  "scripts": {
    "start": "./node_modules/.bin/tsc --project ./tsconfig.json --watch & ts-node-dev src/index.ts"
  },
  "dependencies": {
    "express": "4.17.1"
  },
  "devDependencies": {
    "ts-node-dev": "1.1.8",
    "typescript": "4.4.2",
    "@types/express": "4.17.13"
  },
  "keywords": []
}

Node version: v12.18.4 NPM version: 6.14.13

What am I missing here? Please help!

Upvotes: 0

Views: 3618

Answers (2)

Brenn
Brenn

Reputation: 1384

This won't work, because Express doesn't know about your AuthRequest class, only Request.

You have two options. The first is to extend the Request object using @types: Extend Express Request object using Typescript

Then you can add user as an optional member. Then you can do the following:

const RequireAuth = (middlewarefn: AuthMiddleware) => (req:Request, res:Response, next:NextFunction) => => {
  if(req.user) {
     return middlewarefn(req as AuthRequest, res, next)
  }
  // throw error?  404?
  next();
}

You'd have to wrap your authenticated functions but it would give you the signature you need. You are violating the middleware API the way you are doing it, so you need to adapt to your new method signature.

There's a few other ways to approach this from a practical standpoint, if your goal is to ensure that 'user' exists.

Upvotes: 1

MichalRsa
MichalRsa

Reputation: 360

Instead of extending Request interface, create custom.d.ts and augment existing Request interface.

import { User} from "./interface";

declare module "express-serve-static-core" {
  export interface Request {
    user?: User;
  }
}

If it will not work check answers in following page containing the same problem Extend Express Request object using Typescript

Upvotes: 2

Related Questions