简体   繁体   English

GraphQL:几个字段解析器

[英]GraphQL: several field resolver

everyone!每个人!

Has anyone met such case in graphql: you need resolve several fields with several async sources like this?有没有人在 graphql 中遇到过这种情况:您需要用这样的多个异步源解析多个字段吗?

type Entity {
  # ---- dataSource1
  fieldA: String
  fieldB: String
  # ---- dataSource2
  fieldC: String
  fieldD: Stirng
}

So fieldA and fieldB need to be resolved by dataSource1, and fieldC and fieldD - by dataSource2所以 fieldA 和 fieldB 需要由 dataSource1 解析,而 fieldC 和 fieldD - 由 dataSource2 解析

I found 2 solutions, but it seem to be unnatural for graphql and looks like hack.我找到了 2 个解决方案,但对于 graphql 来说似乎是不自然的,并且看起来像 hack。

Does anyone knows more convinient way to solve it?有谁知道更方便的方法来解决它? Appreciate your help!感谢你的帮助!

PS Fields need to be resolved on the same field level; PS 字段需要在同一个字段级别解析;

1st - is to check dataSource2 attributes in the query's AST and include its call or not:第一个 - 是检查查询的 AST 中的 dataSource2 属性并包括它的调用与否:

Query: {
  Entity: async (_, args, context, info) => {
    const result = {};
    
    if (hasArrts(info, SOURCE_ONE_ARGS)) {
      const data = await dataSource1.load(args);
      Object.assign(result, data);
    }
    if (hasArrts(info, SOURCE_TWO_ARGS)) {
      const data = await dataSource2.load(args);
      Object.assign(result, data);
    }
    
    return result;
  }
}

2nd - is to manage it with graphql/dataloader like this (it is possible due to caching and promise scheduling in the dataloader)第二 - 是像这样使用graphql / dataloader管理它(由于数据加载器中的缓存和promise调度,这是可能的)

Query: {
  Entity: {
    fieldA: async (_, args) => {
      const { fieldA } = await dataSource1.load(args);
    
      return fieldA;
    },
    fieldB: async (_, args) => {
      const { fieldB } = await dataSource1.load(args);
    
      return fieldB;
    },
    fieldC: async (_, args) => {
      const { fieldC } = await dataSource2.load(args);
      
      return fieldC;
    },
    fieldD: async (_, args) => {
      const { fieldD } = await dataSource2.load(args);
      
      return fieldD;
    },
  },
};

The "correct answer" (quotes here, because this is my opinion) is definitely number 2. One of the principles I teach with GraphQL is that you should never be doing work on the server that the client isn't SPECIFICALLY asking for. “正确答案”(这里引用,因为这是我的意见)绝对是第二位。我用 GraphQL 教授的原则之一是,你永远不应该在客户端没有特别要求的服务器上工作。 That means if the client is only ever asking for fields A & B, which come from DataSource1, you're doing all of the work to call DataSource2 for absolutely no reason.这意味着如果客户端只要求来自 DataSource1 的字段 A 和 B,那么您正在做所有的工作来调用 DataSource2 绝对没有理由。

Some extra points here:这里有一些额外的点:

  1. Generally in cases like this, there is "one source of truth for the object", and "an extra service to get other properties".通常在这种情况下,有“对象的一个真实来源”和“获取其他属性的额外服务”。 The source of truth will usually be called by the parent resolver to pull the object, and the "extra service" would be called only in the resolvers that needs the "extra lookup".真相源通常会被父解析器调用来拉取object,而“额外服务”只会在需要“额外查找”的解析器中被调用。

  2. Some people take this to an extreme and ONLY use field-level resolvers .有些人将其发挥到极致, 只使用字段级解析器

  3. If you do actually do what that blog post suggests, please be careful that you're not using a "dummy resolver" as the parent resolver like he does.如果您确实按照该博客文章的建议进行操作,请注意不要像他那样使用“虚拟解析器”作为父解析器。 It just returns { id: <id> } without actually calling the source of truth to ensure the object actually exists.它只是返回{ id: <id> }而不实际调用事实源以确保 object 确实存在。 This means that if the object doesn't actually exist , you would return an object with null properties instead of returning the null as the object (since it doesn't actually exist). This means that if the object doesn't actually exist , you would return an object with null properties instead of returning the null as the object (since it doesn't actually exist).

If you go with all of this, you can go two ways:如果你 go 这一切,你可以 go 两种方式:

Check for existence only:仅检查是否存在:

const resolver = {
  Query: {
    someEntity: async (_, args, context) => {
      // Super light-way check for existence without loading any fields
      const exists = dataSource1.exists(args.id);

      if (!exists) {
        return null;
      }
      return { id: args.id };
    },
  },
  Entity: {
    fieldA: async (_, args) => {
      const { fieldA } = await dataSource1.load(args);
    
      return fieldA;
    },
    fieldB: async (_, args) => {
      const { fieldB } = await dataSource1.load(args);
    
      return fieldB;
    },
    fieldC: async (_, args) => {
      const { fieldC } = await dataSource2.load(args);
      
      return fieldC;
    },
    fieldD: async (_, args) => {
      const { fieldD } = await dataSource2.load(args);
      
      return fieldD;
    },
  },
};

Load from source of truth first:首先从事实来源加载:

const resolver = {
  Query: {
    someEntity: async (_, args, context) => {
      return datasource1.load(args.id);
    },
  },
  Entity: {
    fieldC: async (_, args) => {
      const { fieldC } = await dataSource2.load(args);
      
      return fieldC;
    },
    fieldD: async (_, args) => {
      const { fieldD } = await dataSource2.load(args);
      
      return fieldD;
    },
  },
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM