【问题标题】:How can I use express middleware with only Apollo Server 2 graphql endpoints如何仅使用 Apollo Server 2 graphql 端点的快速中间件
【发布时间】:2019-05-02 18:06:43
【问题描述】:

我想对我的所有路由使用morgantiny 日志语句,graphql 端点除外。我正在使用 express 和 Apollo 2,但无法让中间件与 express 一起使用。如代码示例所示,我可以为整个 express 应用安装中间件,但我想限制范围。

我的第一次尝试是创建一个express.router() 并将路由器传递给apolloServer.applyMiddleware,但这似乎不起作用。

我想使用morgan--但我也想使用express-jwt 中间件。

import morgan from 'morgan'
import { mergeSchemas } from 'graphql-tools'
import { ApolloServer } from 'apollo-server-express'

import assessmentSchema from './assessment/schema'
import AssessmentAPI from './assessment/dataSource'

import userSchema from './user/schema'
import UserAPI from './user/dataSource'

/**
 * Installs apollo-server to handle requests under `path`
 * @param {*} app Express instance
 * @param {*} path route path, like '/graphql'
 */
export const createApi = (app, path) => {
  const dataSources = () => ({
    assessmentAPI: new AssessmentAPI({ store: 'intentionally undefined' }),
    userAPI: new UserAPI()
  })

  const schema = mergeSchemas({
    schemas: [assessmentSchema, userSchema]
  })

  morgan.token('graphql-query', req => {
    const { operationName } = req.body
    return `GRAPHQL: Operation Name: ${operationName}`
  })

  // TODO: Add custom logging middleware for GraphQL queries/mutations
  // The next line would add middleware to all of express, but I only want this style of logging for graphQL

  /*** Question is about the following line ***/
  // app.use(morgan(':graphql-query'))

  const apolloServer = new ApolloServer({ schema, dataSources })
  apolloServer.applyMiddleware({ app, path })
}

谢谢!

【问题讨论】:

标签: node.js express graphql apollo apollo-server


【解决方案1】:

有一些“Hacky”方法可以实现您的愿望。您可以使用 express.Route 在每个路由上注册中间件,但我认为您可能需要更具体的关于 GraphQL 的日志,而不是特别的请求。

上下文()

可作为 ApolloServer 内部的回调,它接收带有请求和响应的对象。

const myServer =  new ApolloServer({
  schema: ...,
  context:({ req, res }) => { 
    // log here
  }
});

格式响应()

可作为 ApolloServer 内部的回调,它接收响应和查询。

const server = new Apollo.ApolloServer({
  schema: ...,
  formatResponse: (res, query) => {
    // log here

    // notice you must return the response
    return res;
  },
});

来源: formatResponse, context

编辑

您可以做的另一件事是在 morgan 回调中检查 req.path 是否与 /graphQL 路径匹配,并仅在这种情况下登录,但这与使用 morgan 记录 Express.Route 非常相似

【讨论】:

  • 我同意这些是可以在每个 graphql 请求中执行代码的钩子(在请求之前通过 context() 或在请求之后通过 formatResponse(),但两者都不是为了支持 express 中间件而设计的。跨度>
  • 我所做的是将req 添加到上下文对象中。到目前为止,这似乎是我想出的最佳选择。 context: ({req, res}) => ({req, res}) 这使得从 graphql 解析器和数据源中访问 express req/res 成为可能。
  • 是的,我也想过这个问题,我也编辑了答案,添加了另一种方法,但我认为与上面的解决方案相同
  • 你的方法是迄今为止最好的答案——但我真的希望有一些明显的方法可以将中间件与 Apollo Server 一起使用(我只是不知道它是什么)也许其他人会提供另一个答案 - 或者它只是不可能的。
  • 如何将 next() 放入中间件?我尝试在上下文 args 中解构它,但它出现未定义。
【解决方案2】:

使用 apollo server v2,仅在单个路由上使用它真的很简单。应用它作为中间件。 即

const app = require('express')();
const apolloServer = new ApolloServer ({
   typeDefs,
   resolvers
}) 

// then use it on a particular route

apolloServer.applyMiddleware({ app, path: '/specialUrl' });

【讨论】:

    【解决方案3】:
    const express = require("express");
    const router = express.Router();
    const { ApolloServer, gql } = require('apollo-server-express');
    
    const server = new ApolloServer({
        schema: schema,
        introspection: true
    }); 
    
    server.applyMiddleware({ app:router });
    
    module.exports = router;
    

    【讨论】:

    • 这个解决方案适合我
    【解决方案4】:

    有一个npm包GraphQL-Router-Ware

    使用它,您可以将路由器级别的中间件添加到您的解析器中,就像我们在 express 中所做的那样。

    您可以像这样设置解析器:

    import Router from 'graphql-router-ware';
    import { checkPermission } from '../helpers/userhalper';
    import Controller from '../controllers/page';
    
    const resolvers = {
        Query: {
            singlePage: Router(Controller.singlePage)
        },
        Mutation: {
            createPage: Router(checkPermission,Controller.create),
            updatePage: Router(checkPermission,Controller.update),
        }
    }
    
    export default resolvers;
    

    接着是你的中间件:

    // ....
    export checkPermission=({ ctx },next)=>{
    
        if(!ctx.user){
            return next(new Error('Not logged in'));
            // or throw new Error('Not logged in')
        }
    
        // some more permission checks....
        return next();
    };
    // ....
    

    希望这对您有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-03
      • 2020-09-29
      • 2020-06-12
      • 2021-09-09
      • 2021-12-04
      • 2018-12-14
      • 2019-06-08
      • 2019-05-19
      相关资源
      最近更新 更多