【问题标题】:Notable differences between buildSchema and GraphQLSchema?buildSchema 和 GraphQLSchema 之间的显着区别?
【发布时间】:2019-05-27 18:54:47
【问题描述】:

两者之间有什么明显的区别吗?我对从运行时和启动性能到功能和工作流程差异的任何事情都感兴趣。文档在解释差异以及何时应该使用另一个方面做得很差。

两个版本的示例:

构建架构

const { graphql, buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

const root = { hello: () => 'Hello world!' };

graphql(schema, '{ hello }', root).then((response) => {
  console.log(response);
});

GraphQLSchema

const { graphql, GraphQLSchema, GraphQLObjectType, GraphQLString } = require('graphql');

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: () => ({
      hello: {
        type: GraphQLString,
        resolve: () => 'Hello world!'
      }
    })
  })
});

graphql(schema, '{ hello }').then((response) => {
  console.log(response);
});

【问题讨论】:

    标签: graphql graphql-js


    【解决方案1】:

    buildSchema 函数采用 SDL(模式定义语言)中的模式并返回一个 GraphQLSchema 对象。给定每个方法生成的两个相同模式,运行时性能将是相同的。使用 buildSchema 的服务器的启动时间会更慢,因为解析 SDL 会增加一个额外的步骤,否则会不存在 - 是否会有明显差异,我无法确定。

    通常不建议使用buildSchema,因为它会严重限制架构的功能。

    使用buildSchema 生成的架构:

    • 无法为单个字段指定解析函数
    • 无法为类型指定resolveTypeisTypeOf 属性,导致无法使用UnionsInterfaces
    • 无法使用自定义标量

    第 1 项强调不够——buildSchema 不允许您为架构中的任何字段指定解析器函数。这包括您的 QueryMutation 类型上的字段。使用 buildSchema 的示例通过依赖 GraphQL 的默认解析器行为并传入 root 值来解决此问题。

    默认情况下,如果一个字段没有指定 resolve 函数,GraphQL 将检查父值(由父字段的解析器返回)并且(假设它是一个对象)将尝试在该父项上查找一个属性与字段名称匹配的值。如果找到匹配项,则将字段解析为该值。如果匹配恰好是一个函数,它首先调用该函数,然后解析为该函数返回的值。

    在上面的示例中,第一个模式中的 hello 字段没有解析器。 GraphQL 查看父值,对于 根级别字段,它是传入的 root 值。根值有一个名为 hello 的字段,它是一个函数,所以它调用函数,然后解析为函数返回的值。只需将 hello 属性设为 String 而不是函数,即可达到相同的效果。

    鉴于上述情况,问题中的两个示例实际上相同。相反,我们必须像这样修改第二个模式以使其等效:

    const schema = new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'Query',
        fields: () => ({
          hello: {
            type: GraphQLString,
          }
        })
      })
    });
    
    const root = { hello: () => 'Hello world!' };
    
    graphql(schema, '{ hello }', root).then((response) => {
      console.log(response);
    });
    

    虽然通过根传递解析器是一个巧妙的技巧,但它仅适用于根级字段(如 QueryMutationSubscription 类型上的字段)。如果您想为不同类型的字段提供解析器,则无法使用buildSchema

    底线:不要使用buildSchema

    但我想使用 SDL!

    你仍然可以! 但是...不要使用普通的 GraphQL.js。相反,如果您想利用 SDL 生成架构,您应该改用 graphql-tools' makeExecutableSchema 或使用更完整的解决方案,如 apollo-server,它在后台使用 makeExecutableSchemamakeExecutableSchema 允许您使用 SDL 定义模式,同时还提供单独的 resolvers 对象。所以你可以这样做:

    const typeDefs = `
      type Query {
        hello: String
      }
    `
    
    const resolvers = {
      Query: {
        hello: () => 'Hello!',
      },
    }
    
    const schema = makeExecutableSchema({ typeDefs, resolvers })
    

    不同之处在于,与buildSchema 不同,您还可以为其他类型提供解析器,甚至为您的接口或联合提供resolveType 属性。

    const resolvers = {
      Query: {
        animals: () => getAnimalsFromDB(),
      }
      Animal: {
        __resolveType: (obj) => obj.constructor.name
      },
      Cat: {
        owner: (cat) => getOwnerFromDB(cat.ownerId),
      }
    }
    

    使用makeExecutableSchema,您还可以实现自定义标量和模式指令,轻松自定义各种模式验证规则,甚至允许实现类型从其接口继承解析器。虽然了解 GraphQL.js 的基础知识以及如何使用GraphQLSchema 构造函数生成基本模式至关重要,但makeExecutableSchema 是一个更完整、更灵活的解决方案,应该是大多数项目的首选。 See the docs 了解更多详情。

    更新

    如果您一心想要使用buildSchema,实际上可以通过使用 ES6 类来解决无法为非根类型提供解析器的问题。查看this sample schema。这并没有解决buildSchema 的所有其他限制,但它确实使它更可口。

    【讨论】:

    • 我在 vanilla buildSchema 和 JS TBH 上做得很好。添加框架或库的开销对我来说真的没有任何作用。老实说,我不需要单个字段的解析器
    猜你喜欢
    • 1970-01-01
    • 2018-05-03
    • 2023-03-30
    • 2021-12-08
    • 1970-01-01
    • 2020-03-09
    • 1970-01-01
    • 2021-12-25
    相关资源
    最近更新 更多