在GraphQL的根目录解析自定义类型

时间:2019-09-24 00:48:22

标签: javascript node.js graphql

我觉得我缺少明显的东西。我将ID存储为[String],希望能够解析为它们代表的完整对象。

背景

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

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',
  },
];


所需结果

我希望能够在 DATA 中存在[Book]的任何地方将[String]放入 SCHEMA 中,并让书本从中加载这些字符串。像这样:

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触发,这使我认为应该可行,但是我在网上找不到答案,这似乎很不错。常见用例,但我在任何地方都找不到实现细节。

我调查的内容

  • 架构指令似乎太过符合我的要求,我只想能够在数据中实际存在[Books]的任何地方插入[String],而不必在每个地方都[Books] @rest('/external/api')
  • 架构委派。在我的用例中,使Books公开可查询并不是真正合适的选择,只会使我的Public模式与未使用的Queries杂乱无章。

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

1 个答案:

答案 0 :(得分:0)

如果有帮助,您可以这样考虑:类型描述响应中返回的数据的种类,而字段描述数据的实际。考虑到这一点,只有一个 field 可以有一个解析程序(即一个告诉它解析什么样的值的函数)。 类型的解析器在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(听起来就像在做的那样),那么通过这种方法将不会真正获得任何收益。