【问题标题】:Resolve Custom Types at the root in GraphQL在 GraphQL 的根部解析自定义类型
【发布时间】:2019-09-24 10:14:17
【问题描述】:

我觉得我错过了一些明显的东西。我有存储为 [String] 的 ID,我希望能够解析它们所代表的完整对象。

背景

这就是我想要启用的。缺少的成分是解析器:

const bookstore = `
  type Author {
    id: ID!
    books: [Book]
  }

  type Book {
    id: ID!
    title: String
  }

  type Query {
    getAuthor(id: ID!): Author
  }
`;

const my_query = `
  query {
    getAuthor(id: 1) {
      books { /* <-- should resolve bookIds to actual books I can query */
        title
      }
    }
  }
`;

const REAL_AUTHOR_DATA = [
  {
    id: 1,
    books: ['a', 'b'],
  },
];

const REAL_BOOK_DATA = [
  {
    id: 'a',
    title: 'First Book',
  },
  {
    id: 'b',
    title: 'Second Book',
  },
];


想要的结果

我希望能够将[Book] 放在SCHEMA 中任何[String] 存在于DATA 中的位置,并让书籍从这些字符串中自行加载。像这样的:

const resolve = {
  Book: id => fetchToJson(`/some/external/api/${id}`),
};

我的尝试

这个解析器什么都不做,console.log 甚至没有被调用

const resolve = {
  Book(...args) {
    console.log(args);
  }
}

但是,这确实得到了一些结果...

const resolve = {
  Book: {
    id(id) {
      console.log(id)
      return id;
    }
  }
}

console.log确实在哪里发出 'a''b'。但我显然无法将其扩展到 X 个字段,这太荒谬了。

我的团队目前所做的是从父母那里解决它:

const resolve = {
  Author: {
    books: ({ books }) => books.map(id => fetchBookById(id)),
  }
}

这并不理想,因为也许我有一个type Publisher { books: [Book]} 或一个type User { favoriteBooks: [Book] } 或一个type Bookstore { newBooks: [Book] }。在每种情况下,后台的数据实际上是 [String],我不想重复这段代码:

const resolve = {
  X: {
    books: ({ books }) => books.map(id => fetchBookById(id)),
  }
};

定义Book.id 解析器导致console.log 实际触发的事实让我认为这应该是可能的,但我在网上的任何地方都找不到我的答案,这似乎是一个很常见的用途案例,但我在任何地方都找不到实现细节。

我调查了什么

  • Schema Directives 对于得到我想要的东西似乎有点矫枉过正,我只想能够将[Books] 插入到数据中实际存在[String] 的任何位置,而不必在每个单独的位置都执行[Books] @rest('/external/api')地点。
  • 架构委派。在我的用例中,将 Books 设为可公开查询并不合适,只会使我的 Public 架构与未使用的查询混淆。

感谢您阅读本文。希望有一个我忽略的简单解决方案。如果不是,那GQL你为什么会这样……

【问题讨论】:

    标签: javascript node.js graphql


    【解决方案1】:

    如果有帮助,您可以这样想:类型描述响应中返回的数据的种类,而字段描述数据的实际。考虑到这一点,只有 field 可以有一个解析器(即一个告诉它要解析为哪种值的函数)。 type 的解析器在 GraphQL 中没有意义。

    所以,你可以:

    1.处理重复。 即使您有十种不同的类型,它们都具有需要以相同方式解析的books 字段,也没有什么大不了的。显然,在生产应用程序中,您不会将数据存储在变量中,并且您的代码可能会更复杂。但是,通用逻辑可以很容易地提取到一个函数中,可以跨多个解析器重用:

    const mapIdsToBooks = ({ books }) => books.map(id => fetchBookById(id))
    
    const resolvers = {
      Author: {
        books: mapIdsToBooks,
      },
      Library: {
        books: mapIdsToBooks,
      }
    }
    

    2。取而代之的是在根级别获取所有数据。 无需为 books 字段编写单独的解析器,您可以在 getAuthor 解析器中返回作者及其书籍:

    function resolve(root, args) {
      const author = REAL_AUTHOR_DATA.find(row => row.id === args.id)
      if (!author) {
        return null
      }
      return {
        ...author,
        books: author.books.map(id => fetchBookById(id)),
      }
    }
    

    在处理数据库时,这通常是更好的方法,因为它减少了您对数据库发出的请求数量。但是,如果您要包装现有的 API(这听起来就像您正在做的那样),那么走这条路线您将不会真正获得任何收益。

    【讨论】:

      猜你喜欢
      • 2020-10-04
      • 2018-03-18
      • 2021-12-22
      • 2017-08-13
      • 2016-10-21
      • 2020-07-21
      • 2019-09-29
      • 2017-09-18
      • 1970-01-01
      相关资源
      最近更新 更多