gpt4 book ai didi

graphql - graphql中resolver函数不同实现的解释

转载 作者:行者123 更新时间:2023-12-03 16:23:40 31 4
gpt4 key购买 nike

我一直在阅读graphQL文档,发现他们以两种方式解释了graphql服务器的实现:一种使用graphql-yoga,它是一个功能齐全的graphql服务器,另一种使用graphql、express-graphql和express .在这两种情况下,我们在创建服务器实例时传递架构和解析器函数。

但是解析器函数的实现不同。在使用 graphql-yoga 时,解析器函数提供了 4 个参数,其中包含有关父对象的信息、接收的参数、上下文、信息。而在另一种情况下(使用 graphql),解析器函数仅获取参数对象。

为什么呢 ?如果我想要信息、上下文对象,我该如何获取?

使用 graphql-yoga 示例:https://graphql.org/learn/execution/

使用 graphql 示例:https://graphql.github.io/graphql-js/mutations-and-input-types/

//使用graphql的代码示例

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');

var schema = buildSchema(`
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
type Mutation {
addDice(numDice: Int): String
}
`);

var root = {
rollDice({numDice, numSides}) {
return [1, 2];
},
addDice({numDice}) {
console.log("Adding something");
return "Added";
}
};

var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

//使用graphql-yoga的代码示例
let graphqlServer = require("graphql-yoga");

const typeDefs = `
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
type Mutation {
addDice(numDice: Int): String
}
`;

const resolvers = {
Query: {
rollDice(parent, args, context, info) {
console.log(args.numDice);
console.log(args.numSides);
return [1, 2];
}
},
Mutation: {
addDice(parent, args, context, info) {
console.log(args.numDice);
return "Added";
}
}
};

const server = new graphqlServer.GraphQLServer({
typeDefs,
resolvers
});

server.start(() => {
console.log("server started on localhost:4000");
});

这两个代码片段之间的区别:

在一种情况下,解析器函数存在于适当的类型(即查询、变异)中。在另一种情况下,它们存在于一个根对象中。这意味着在第一种情况下我可以在 Query 和 Mutation 中使用同名的方法,而在第二种情况下这是不可能的,因为它们是单个对象的键并且键应该是唯一的。

为什么会这样?我基本上错过了什么吗?一个包与另一个包的实现细节有何不同?

最佳答案

实谈 :GraphQL.js 文档并不是那么好。在我看来,他们永远不应该使用 buildSchema 的例子。首先,因为它会导致这种困惑是可以理解的。

GraphQL.js(即 graphql 包)是 GraphQL 的 JavaScript 实现。通过构建 GraphQLSchema 的实例,以编程方式在 GraphQL.js 中构建模式。类(class):

const userType = new GraphQLObjectType({
name: 'User',
fields: {
id: {
type: GraphQLID,
},
email: {
type: GraphQLString,
},
},
});
const queryType = new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
resolve: () => ({ id: 1, email: 'john.doe@example.com' }),
},
},
});
const schema = new GraphQLSchema({
query: queryType,
})

如果我们用架构定义语言 (SDL) 打印这个架构,它看起来像这样:
type Query {
user: User
}

type User {
id: ID
email: String
}

使用 SDL 比编写所有代码要容易得多。 但是,GraphQL.js 没有提供从 SDL 构建功能齐全的模式的方法。 它确实提供了一个 buildSchema函数,但此实用程序构造了一个模式 没有任何解析器 (以及许多其他功能,如联合/接口(interface)类型解析)。
graphql-tools包提供了 makeExecutableSchema使您可以从 SDL 和解析器映射对象构建架构的函数。这就是 apollo-server 在幕后使用的东西和 graphql-yoga . makeExecutableSchema使用 buildSchema 从 SDL 构造模式然后改变结果对象,在 after the fact 中添加解析器.

在 GraphQL.js 中, resolve字段的函数(或解析器)有四个参数——父值、字段的参数、上下文和 GraphQLResolveInfo目的。如果我们正在创建一个 GraphQLObjectType喜欢 userType在上面的例子中,这是我们可以为对象中的每个字段提供的可选函数。这是 您在构建解析器映射以用于 graphql-yoga 时定义的函数. 这是字段解析器的唯一实现。

那么如何处理 buildSchema ??

文档中的示例利用了 GraphQL 的 default field resolver :
export const defaultFieldResolver: GraphQLFieldResolver<any, *> = function(
source,
args,
contextValue,
info,
) {
if (typeof source === 'object' || typeof source === 'function') {
const property = source[info.fieldName];
if (typeof property === 'function') {
return source[info.fieldName](args, contextValue, info);
}
return property;
}
};

如您所见,默认解析逻辑会查找与源(父)值上的字段同名的属性。在我们上面的例子中, user解析器返回 {id: 1, email: 'john.doe@example.com'} -- 这是字段解析为的值。该字段的类型为 User .我们没有为 id 定义解析器字段,所以默认解析器会做它的事情。 id字段解析为 1因为这是名为 id 的属性的值在解析器接收的父对象上。

但是,父值也可以是函数而不是对象。如果它是一个函数,则首先调用它,然后使用返回值。该函数用什么调用?好吧,它不能向它传递父值(因为无限递归),但它可以向它传递剩余的三个参数(args、context 和 info)。所以这就是它的作用。

现在是魔术 🎩🐇

在我们的示例中,我可以省略 user 的解析器。字段并将函数传递给根值。
const root = {
user: () => ({id: 1, email: 'john.doe@example.com'})
}

根对象只是一个可选对象,它作为父值传递给根级别的解析器(如您的 QueryMutation 类型)。否则,这些解析器将没有父值。
Query是一个可操作的根类型——它充当架构其余部分的“入口点”。 Query 上的任何字段type 将作为父值传递给根对象。如果我省略了 user 的解析器字段,默认解析器将 1) 检查具有相同名称的属性的父对象,2) 找到一个属性并确定它是一个函数,3) 调用该函数,4) 将该字段解析为函数的返回值.

多田!

但是,由于该函数是由默认解析器调用的,并且本身并不用作解析器,因此它只会接收上述三个参数,而不是 4 个。

这是解决无法为模式实际提供自定义解析器的一种巧妙方法,但它非常有限。它只适用于根类型,所以我们不能类似地为 User 提供假解析器。字段或其他类型。我们不能在我们的模式中使用接口(interface)或联合,因为我们不能提供 resolveType职能。等等...

希望这提供了一些清晰度。希望我们可以在不久的将来更新文档,以避免所有这些困惑。

关于graphql - graphql中resolver函数不同实现的解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55854430/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com