如何在apollo服务器v4上正确传递用户并运行中间件

问题描述 投票:0回答:1

我正在创建一个带有express.js集成的apollo服务器v4 gql网关。 我有用户 typedef 和用户解析器工作并在创建用户时创建 jwt。 然后,我尝试在

jwtMiddleware
端点上使用我的
/graphql
中间件,以便它可以验证传入的不记名令牌,以防止对我创建的其他一些查询发出未经身份验证的请求。

在我的index.ts中:

import express, { Request, Response, Application } from 'express';
import gqlServerV1 from './gql/v1/gqlServerV1';
import { mongoClient } from './mongodb/mongoclient';
import cors from 'cors';
import pkg from 'body-parser';
import { expressMiddleware } from '@apollo/server/express4';
import jwtMiddleware from './middleware/verifyJwt';
const { json } = pkg;

// Create express app
const app: Application = express();
const port: number = 5950;
const startUp = async () => {
  // Fire up mongodb client
  await mongoClient();

  // Apply gql server to work with express as middleware w/ apollo-server v4
  // Migrate away from v3: https://www.apollographql.com/docs/apollo-server/migration/#migrate-from-apollo-server-express
  await gqlServerV1.start();

  app.use(
    '/v1/graphql',
    cors<cors.CorsRequest>(),
    json(),
    jwtMiddleware,
    expressMiddleware(gqlServerV1, {
      context: async ({ req }) => ({
        token: req.headers.token,
        user: req.user,
      }),
    })
  );

  console.log(`server started at http://localhost:${port}`);
};

app.listen(port, async () => {
  await startUp();
});

jwt中间件文件

import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

dotenv.config();

const JWT_SECRET = process.env.JWT_SECRET || 'defaultSecret';

const jwtMiddleware = async (req, res, next, context) => {
  const authorizationHeader = req.headers.authorization;

  console.log('heress');

  if (!authorizationHeader || !authorizationHeader.startsWith('Bearer ')) {
    // Token is missing or in an invalid format
    return next();
  }

  const token = authorizationHeader.replace('Bearer ', '');

  try {
    // Verify the JWT token
    const decodedToken = jwt.verify(token, JWT_SECRET);

    console.log(decodedToken);

    // Get the user data from the request context
    const user = context.user;

    // Continue processing the request if the user is authenticated
    if (user) {
      next();
    } else {
      // The user is not authenticated, so return an error
      return res.status(401).json({ message: 'Unauthorized' });
    }
  } catch (error) {
    // The JWT token is invalid, so return an error
    return res.status(401).json({ message: 'Invalid token' });
  }
};

export default jwtMiddleware;

我面临的问题:

启动服务器时,我在通过

index.ts
 时看到此错误来自 
req.user

TSError: ⨯ Unable to compile TypeScript:
src/index.ts:37:19 - error TS2339: Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.

37         user: req.user,
  1. 我从未看到中间件输出任何控制台日志。

这是我的 gql 服务器,位于单独的文件中:

import { ApolloServer } from '@apollo/server';
import addressTypeDefs from './typeDefs/addressTypeDefs';
import userTypeDef from './typeDefs/userTypeDef';
import resolvers from './resolvers';

interface MyContext {
  token?: String;
  user?: Object;
}

// Create gql server
const gqlServerV1 = new ApolloServer<MyContext>({
  typeDefs: [addressTypeDefs, userTypeDef],
  resolvers,
  introspection: false,
});

export default gqlServerV1;

打包json脚本:

  "scripts": {
    "start": "ts-node-dev --watch src src/index.ts",
    "dev:server": "nodemon --watch './**/*.ts' --exec 'node --experimental-specifier-resolution=node --loader ts-node/esm' src/index.ts",
    "test": "jest --detectOpenHandles --config jest.config.ts ./__tests__",
    "watch": "nodemon --watch './**/*.{ts,graphql}' --exec 'node --experimental-specifier-resolution=node --no-warnings --loader ts-node/esm' src/index.ts",
    "dev": "tsc-watch --onSuccess \"npm run watch\""
  },

我还有一个 custom.d.ts 文件来尝试扩展express:

interface JwtPayload {
  // Customize the properties based on JWT payload
  email: string;
}

// Extend the express request type
declare namespace Express {
  interface Request {
    user: JwtPayload;
  }
}

  1. 如何正确通过
    req.user
  2. 为什么我的中间件从未运行来验证用户/令牌?

谢谢!

node.js express graphql apollo-server
1个回答
0
投票

我找到了问题所在,并将问题发布出来,以便其他人可以学习。我面临的问题是因为 ts-node 无法解释我扩展的类型并将用户添加到即请求中。这就是我所做的。

  1. root/src/@types/express/index.d.ts
    创建一个新的 dir 和 d.ts 文件。
  2. 在 ts 配置中,确保我的自定义类型位于
    typeRoot
    配置中的任何其他类型之前。像这样:
{
  "compilerOptions": {
    "rootDirs": ["src"],
    "outDir": "dist",
    "lib": ["es2020"],
    "target": "es2020",
    "module": "esnext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "types": ["node", "jest", "express"],
    "typeRoots": ["./src/@types", "./node_modules/@types"],
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  },
  "include": ["src/**/*.ts"]
}

  1. index.d.ts
    内部,我从 mongodb 导入了在
    models
    中设置的自定义界面,并将其设置为如下类型:
import * as express from 'express';
import { User } from '../../models/User';

declare global {
  namespace Express {
    interface Request {
      user?: User;
    }
  }
}

  1. 在我的 verifyJwt 中间件中,我从中间件签名中删除了
    context
    ,并将
    context
    的实例替换为
    req

此后,我重新启动了 ts 服务器以及本地服务器,瞧,它工作了,愚蠢的

Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
错误消失了。我的中间件开始工作,我开始看到来自中间件的控制台日志作为验证。

希望这对其他人有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.