buildSchema和GraphQLSchema之间的显着差异? [英] Notable differences between buildSchema and 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指定
resolveType
或isTypeOf
属性,因此无法使用Unions
和Interfaces
- 无法使用自定义标量
项目#1承受的压力不够大-buildSchema
不允许您为架构中的任何字段指定解析器功能.这包括您的Query
和Mutation
类型的字段.使用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);
});
虽然通过根传递解析器是一个巧妙的技巧,但它再次仅适用于根级别字段(如Query
,Mutation
或Subscription
类型的字段).如果您想为其他类型的字段提供解析器,则无法使用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
orisTypeOf
properties for Types, making it impossible to useUnions
andInterfaces
- 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屋!