【问题标题】:Apollo conditional data sources & initialization lifecycleApollo 条件数据源和初始化生命周期
【发布时间】:2021-12-12 02:37:42
【问题描述】:

我有一个特定的用例,其中用户的数据源是有条件的 - 例如,基于每个特定用户在数据库中保存的数据源。

这也意味着每个数据源对每个用户都有唯一的凭据,这对于 RESTDataSource 来说很好,因为我可以在每个请求之前使用 willSendRequest 设置身份验证标头。

但是,我有具有专有客户端的自定义数据源(例如用于 Salesforce 的 JSForce)——它们有自己的获取机制。

到目前为止 - 我有一个自定义转换器指令,它从数据库中获取令牌并将其添加到上下文中 - 但是,该指令在 dataSource.initialize() 方法之前运行 - 所以我不能使用那里的凭据,因为上下文仍然没有它。

我也不想为每个用户初始化所有数据源,即使他在此请求中没有使用所述数据源 - 但 dataSources() 函数不接受任何参数并且不是上下文的。

底线是 - 是否可以根据 Express 请求有条件地传递数据源?何时将令牌和凭据传递给数据源?也许添加我自己的自定义初始化函数并从指令中调用它?

【问题讨论】:

    标签: graphql apollo apollo-server


    【解决方案1】:

    所以你有选择。这里有 2 个选择:

    1。只需添加您的数据源

    如果你只是初始化所有数据源,它可以在内部检查用户是否有访问权限。您可以有一个 getClient 函数在客户端上解析或抛出 UnauthorizedError,具体取决于。

    2。不要只添加数据源

    所以如果你真的根本不想初始化数据源,你完全可以通过自己添加“数据源”来做到这一点,就像 Apollo 一样。

    const server = new ApolloServer({
      // this example uses apollo-server-express
      context: async ({ req, res }) => {
        const accessToken = req.headers?.authorization?.split(' ')[1] || ''
        const user = accessToken && buildUser(accessToken)
    
        const context = { user }
    
        // You can't use the name "dataSources" in your config because ApolloServer will puke, so I called them "services"
        await addServices(context)
        return context
      }
    })
    
    const addServices = async (context) => {
      const { user } = context;
      const services = {
        userAPI: new UserAPI(),
        postAPI: new PostAPI(),
      }
    
      if (user.isAdmin) {
        services.adminAPI = new AdminAPI()
      }
    
      const initializers = [];
      for (const service of Object.values(services)) {
        if (service.initialize) {
          initializers.push(
            service.initialize({
              context,
              cache: null, // or add your own cache
            })
          );
        }
      }
    
      await Promise.all(initializers);
    
      /**
       * this is where you have to deviate from Apollo.
       * You can't use the name "dataSources" in your config because ApolloServer will puke
       * with the error 'Please use the dataSources config option instead of putting dataSources on the context yourself.'
       */
      context.services = services;
    }
    

    一些注意事项:

    1。你不能称它们为“数据源”

    如果您在上下文对象上返回一个名为“dataSources”的属性,Apollo 将不会非常喜欢它 [meaning it throws an Error]。在我的示例中,我使用了名称“服务”,但您可以为所欲为……除了“数据源”。

    使用上面的代码,在您的解析器中,只需引用 context.services.whatever

    2。这就是阿波罗所做的

    此模式直接复制自 Apollo 已经为数据源所做的工作 [source]

    3。我建议您仍然将它们视为数据源

    我建议您坚持 DataSources 模式,并且您的“服务”都扩展 DataSource。参与的每个人都会变得更容易。

    4。类型安全

    如果您使用 TypeScript 或其他东西,您将失去一点类型安全性,因为 context.services 要么是一种形状,要么是另一种形状。即使你不是,如果你不小心,你最终可能会抛出“无法读取未定义的属性用户”错误而不是“未经授权”错误。您最好创建反映相同对象形状但只是抛出未经授权的“虚拟服务”。

    【讨论】:

    • 惊人的答案,非常感谢:)
    猜你喜欢
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 2019-02-08
    • 2015-03-22
    • 1970-01-01
    • 2016-12-25
    • 2022-08-03
    • 2018-06-17
    相关资源
    最近更新 更多