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

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

问题描述

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

两个版本中的示例:

buildSchema

 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会增加一个否则不存在的额外步骤-无论是否存在值得注意的差异,我不能确切地说. /p>

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

使用buildSchema生成的模式:

  • 无法为单个字段指定解析功能
  • 无法为Types指定resolveTypeisTypeOf属性,因此无法使用UnionsInterfaces
  • 无法使用自定义标量

项目#1承受的压力不够大-buildSchema不允许您为架构中的任何字段指定解析器功能.这包括您的QueryMutation类型的字段.使用buildSchema的示例通过依赖GraphQL的默认解析器行为并传递root值来解决此问题.

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

在上面的示例中,第一个架构中的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),该解决方案在后台使用了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是一个更完整,更灵活的解决方案,应该是大多数项目的首选. 有关详细信息,请参阅文档.

更新

如果您热衷于使用buildSchema,则实际上有可能通过使用ES6类来解决无法为非根类型提供解析器的问题.查看此示例模式.这不能解决buildSchema的所有其他限制,但是确实使其更可口.

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.

Example in both versions:

buildSchema

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

解决方案

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.

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

A schema generated using buildSchema:

  • 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 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.

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.

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

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.

Bottom line: don't use buildSchema.

But I wanted to use SDL!

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

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

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.

UPDATE

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天全站免登陆