buildSchema 和 GraphQLSchema 之间的显着差异? [英] Notable differences between buildSchema and GraphQLSchema?

查看:24
本文介绍了buildSchema 和 GraphQLSchema 之间的显着差异?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Are there any notable differences between the two? Im interested in anything from runtime and startup performance to features and workflow differences. Documentation does a poor job on explaining the difference and when I should use one over the other.

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(模式定义语言)中的模式并返回一个 GraphQLSchema 对象.给定使用每种方法生成的两个相同模式,运行时性能将相同.使用 buildSchema 的服务器的启动时间会更慢,因为解析 SDL 添加了一个原本不存在的额外步骤——是否会存在明显差异,我可以'不能肯定地说.

The buildSchema function takes a schema in SDL (schema definition language) and returns a GraphQLSchema object. Given two identical schemas generated with each method, runtime performance would be the same. Startup time for a server using buildSchema would be slower since parsing the SDL adds an extra step that would not otherwise exist -- whether there would be a noticeable difference, I can't say definitively.

使用 buildSchema 通常是不可取的,因为它严重限制了架构的功能.

Using buildSchema is generally inadvisable, as it severely limits the functionality of your schema.

使用 buildSchema 生成的模式:

  • 无法为单个字段指定解析函数
  • 无法为类型指定 resolveTypeisTypeOf 属性,从而无法使用 UnionsInterfaces
  • 不能使用自定义标量
  • Cannot specify resolve functions for individual fields
  • Cannot specify either resolveType or isTypeOf properties for Types, making it impossible to use Unions and Interfaces
  • Cannot utilize custom scalars

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

Item #1 cannot be stressed enough -- buildSchema does not allow you to specify a resolver function for any field in your schema. This includes fields on your Query and Mutation types. Examples that use buildSchema get around this problem by relying on GraphQL's default resolver behavior and passing in a root value.

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

By default, if a field does not have a resolve function specified, GraphQL will examine the parent value (returned by the parent field's resolver) and (assuming it is an Object) will try to find a property on that parent value that matches the name of the field. If it finds a match, it resolves the field to that value. If the match happens to be a function, it calls that function first and then resolves to the value returned by the function.

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

In the example above, the hello field in the first schema does not have a resolver. GraphQL looks at the parent value, which for root level fields is the root value that is passed in. The root value has a field called hello, and it's a function, so it calls the function and then resolves to the value returned by the function. You could achieve the same effect simply by making the hello property a String instead of a function as well.

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

Given the above, the two examples in the question are actually not the same. Rather, we would have to modify the second schema like this for it to be equivalent:

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

虽然通过根传递解析器是一个巧妙的技巧,但它同样只适用于根级字段(例如 QueryMutation 上的字段订阅 类型).如果您想为不同类型的字段提供解析器,则无法使用 buildSchema 来实现.

While passing in a resolver through the root is a neat trick, again it only works for root level fields (like fields on the Query, Mutation or Subscription types). If you wanted to provide a resolver for a field on a different type, there is no way to do so using buildSchema.

底线:不要使用buildSchema.

但我想使用 SDL!

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

And you still can! But... don't do it using vanilla GraphQL.js. Instead, if you want to utilize SDL to generate your schema, you should instead either use graphql-tools' makeExecutableSchema or use a more complete solution like apollo-server, which uses makeExecutableSchema under the hood. makeExecutableSchema allows you to define a schema using SDL, while also providing a separate resolvers object. So you can do:

const typeDefs = `
  type Query {
    hello: String
  }
`

const resolvers = {
  Query: {
    hello: () => 'Hello!',
  },
}

const schema = makeExecutableSchema({ typeDefs, resolvers })

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

The difference is that, unlike buildSchema, you can also provide resolvers for other types, and even provide resolveType properties for your Interfaces or Unions.

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

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

Using makeExecutableSchema, you can also implement custom scalars and schema directives, easily customize a variety of schema validation rules and even allow implementing types to inherit resolvers from their interfaces. While it's critical to understand the basics of GraphQL.js and how to generate a basic schema using the GraphQLSchema constructor, makeExecutableSchema is a more complete and flexible solution that should be the go-to choice for most projects. See the docs for more details.

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

If you're bent on using buildSchema, it's actually possible to get around the inability to provide resolvers for non-root types by using ES6 classes. Check out this sample schema. This doesn't address all the other limitations of buildSchema, but it does make it more palatable.

这篇关于buildSchema 和 GraphQLSchema 之间的显着差异?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆