【问题标题】:GraphQL nested resolversGraphQL 嵌套解析器
【发布时间】:2018-08-25 08:18:47
【问题描述】:

我是 GraphQL 的新手,我正在尝试设置一个演示应用程序来向我的同事做一些介绍性的演讲。

我正在使用 NodeJS。

我想出了以下架构:

type Query {
  author(id:Int!): Author
  song(id:Int!): Song
}
type Author {
  id:Int!
  name:String!
  songs(max:Int):[Song]!
}
type Song {
  id:Int!
  name:String!
  author:Author!
}

这是与 Song.author 关联相关的解析器部分:

[...]
Song: {
  author: ({ id }, args, context, info) => {
    return mergeInfo.delegate(
      'query',
      'author',
      { id },
      context,
      info,
    );
  }
}
[...]

所以这种方法的问题是我需要将 Song.id 添加到包含查询中才能在其中包含 Song.author:

{
  song(id: 1) {
    id
    author {
      name
    }
  }
}

以下方法不起作用:

{
  song(id: 1) {
    author {
      name
    }
  }
}

根据实施情况,它会给我一个错误或null(那会更糟)。

这迫使编写查询的人知道后端的实现细节,这显然是不好的。 :P

有没有人可以解决这个问题?有什么我忽略的吗?

我尝试使用info 对象,但这会解决问题,因为我需要的 id 是查询的一部分,但我可以想出一个场景,其中我需要的参数位于数据仅在后端可用。

更新:

按照 Daniel 的要求(谢谢),这是创建包含拼接的架构的整个测试文件:

const { makeExecutableSchema, mergeSchemas } = require('graphql-tools');

const DATA = {
  authors: {
    1: { id: 1, name: 'John' },
    2: { id: 2, name: 'Paul' },
  },
  songs: {
    1: { id: 1, name: 'Love me do', authorId: 1 },
    2: { id: 2, name: 'I wanna be your man', authorId: 1 },
    3: { id: 3, name: 'I\'ll be back', authorId: 2 },
  }
};

const authorsTypes = `
  type Query {
    author(id:Int!): Author
  }
  type Author {
    id:Int!
    name:String!
  }
`;
const authorSchema = makeExecutableSchema({
  typeDefs: authorsTypes,
  resolvers: {
    Query: {
      author: (_, { id }) => DATA.authors[id],
    },
  },
});
const authorsLinksTypes = `
  extend type Author {
    songs(max:Int):[Song]!
  }
`;
const authorsLinksResolvers = mergeInfo => ({
  Author: {
    songs: ({ id }, args, context, info) => {
      return Object.values(DATA.songs).filter(it => it.authorId === id)
    }
  },
});

const songsTypes = `
  type Query {
    song(id:Int!): Song
  }
  type Song {
    id:Int!
    name:String!
  }
`;
const songsSchema = makeExecutableSchema({
  typeDefs: songsTypes,
  resolvers: {
    Query: {
      song: (_, { id }) => DATA.songs[id],
    },
  },
});
const songsLinksTypes = `
  extend type Song {
    author:Author!
  }
`;
const songsLinksResolvers = mergeInfo => ({
  Song: {
    author: ({ id }, args, context, info) => {
      return mergeInfo.delegate(
        'query',
        'author',
        { id },
        context,
        info,
      );
    }
  },
});

module.exports = mergeSchemas({
  schemas: [authorSchema, songsSchema, songsLinksTypes, authorsLinksTypes],
  resolvers: mergeInfo => ({
    ...songsLinksResolvers(mergeInfo),
    ...authorsLinksResolvers(mergeInfo),
  }),
});

【问题讨论】:

  • 你能包含你的歌曲查询解析器吗?此外,如果您正在进行模式拼接,在您的问题中包含这些详细信息会很有帮助。
  • 我建议不要在演示中使用模式拼接。模式拼接用于相当大规模的应用程序(例如 IBM 的微服务)。没有它,100 多种类型应该很容易维护。即使这样拼接只是一种解决方案,一些公司还是从它背后的服务中生成他们的类型。如果您想将某些内容分开,请查看 graphcool/import-schema
  • 谢谢建议,demo我绝对不会用拼接

标签: node.js graphql graphql-js


【解决方案1】:

处理此问题的最简单方法是利用上下文传递歌曲 ID。这意味着您需要像这样修改 song 查询的解析器:

Query: {
  song: (_, { id }, context) => {
    context.songId = id
    return DATA.songs[id]
  },
},

然后您可以从作者解析器中的上下文中获取 id

Song: {
  author: (song, args, context, info) => {
    const id = song.id || context.songId
    return mergeInfo.delegate(
      'query',
      'author',
      { id },
      context,
      info,
    );
  }
},

【讨论】:

  • 哇,你能像这样改变上下文吗?很棒
  • 顺便说一句,我遇到了一个问题......当你有集合时会发生什么?如果你创建一个更嵌套的查询,那么很难跟踪哪个是父对象/包含对象某些解析器...
  • 您不应该像那样修改上下文。请参阅以下段落:“作为最佳实践,所有解析器的上下文应该相同,无论特定查询或突变如何,并且解析器永远不应修改它。这确保了解析器之间的一致性,并有助于提高开发速度。”来自apollographql.com/docs/apollo-server/essentials/data#context
猜你喜欢
  • 2019-10-14
  • 2021-07-28
  • 2018-08-27
  • 2021-07-20
  • 2023-04-01
  • 2018-05-16
  • 2019-05-17
  • 2018-09-02
  • 2018-08-05
相关资源
最近更新 更多