buildSchema 和 GraphQLSchema 之间的显着差异?

Notable differences between buildSchema and GraphQLSchema?

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

两个版本中的示例:

构建架构

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);
});

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

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

使用 buildSchema 生成的架构:

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

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

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

在上面的示例中,第一个模式中的 hello 字段没有解析器。 GraphQL 查看父值,对于 root level fields 是传入的 root 值。根值有一个名为 hello,而且它是一个函数,所以它调用函数,然后解析为函数返回的值。您也可以通过将 hello 属性 设为字符串而不是函数来实现相同的效果。

综上所述,问题中的两个例子实际上相同。相反,我们必须像这样修改第二个模式才能使其等效:

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,它在下面使用 makeExecutableSchema引擎盖。 makeExecutableSchema 允许您使用 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 的所有其他限制,但它确实使它更可口。