GraphQL 是一种用于 API 的查询语言和运行时环境,它可以让客户端精确地描述需要的数据,而不需要多次往返服务器。在 GraphQL 中,类型注释是非常重要的一部分,它定义了 API 中的数据类型和字段,帮助客户端准确地理解 API 的结构。然而,在实际开发中,我们可能会遇到类型注释错误的问题,本文将介绍如何解决这个问题。
问题描述
在 GraphQL 中,类型注释通常是通过使用 SDL(Schema Definition Language)来定义的。例如,下面是一个简单的 GraphQL 查询:
query {
user(id: "1") {
name
email
}
}在这个查询中,我们请求了一个名为 user 的字段,这个字段接受一个 id 参数,并且返回一个包含 name 和 email 字段的对象。在 SDL 中,我们可以这样定义这个查询:
type Query {
user(id: ID!): User
}
type User {
name: String!
email: String!
}在这个定义中,我们定义了一个 Query 类型,它包含一个 user 字段,这个字段接受一个 id 参数,并且返回一个 User 类型的对象。User 类型包含 name 和 email 字段,它们的类型都是 String,并且都是必须的。
然而,在实际开发中,我们可能会遇到这样的问题:客户端发送了一个查询,但是服务端返回了一个类型错误的响应。例如,客户端发送了这样一个查询:
query {
user(id: "1") {
name
email
age
}
}在这个查询中,我们请求了一个名为 age 的字段,这个字段在上面的 SDL 中并没有定义。在这种情况下,服务端可能会返回一个错误响应,例如:
// www.javascriptcn.com code example
{
"errors": [
{
"message": "Cannot query field 'age' on type 'User'.",
"locations": [
{
"line": 4,
"column": 5
}
]
}
]
}这个错误告诉我们,客户端请求了一个不存在的字段 age,这个字段在 User 类型中并没有定义。这是一个非常常见的类型注释错误,我们需要解决它。
解决方法
解决类型注释错误的方法很简单,我们只需要检查客户端发送的查询,确保它符合服务端定义的 SDL。如果客户端发送了一个不存在的字段,我们可以在服务端的 SDL 中添加这个字段的定义,或者告诉客户端不要请求这个字段。
例如,在上面的例子中,我们可以在服务端的 SDL 中添加一个 age 字段的定义,例如:
type User {
name: String!
email: String!
age: Int
}在这个定义中,我们将 age 字段的类型定义为 Int,并且它是可选的。这样,当客户端请求 age 字段时,服务端就会返回一个 User 类型的对象,其中包含一个 age 字段,类型为 Int。
另外,我们也可以使用 GraphQL 的指令来指示客户端不要请求某些字段。例如,我们可以在服务端的 SDL 中使用 @deprecated 指令来标记一个字段已经过时,不建议客户端继续使用:
type User {
name: String!
email: String!
age: Int @deprecated(reason: "This field is no longer supported.")
}在这个定义中,我们使用 @deprecated 指令来标记 age 字段已经过时,不建议客户端继续使用。当客户端请求 age 字段时,服务端就会返回一个警告,告诉客户端这个字段已经过时,不应该继续使用。
示例代码
下面是一个使用 Apollo Server 和 Apollo Client 的示例代码,它演示了如何解决类型注释错误:
服务端代码
// www.javascriptcn.com code example
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type Query {
user(id: ID!): User
}
type User {
name: String!
email: String!
age: Int
}
`;
const resolvers = {
Query: {
user: (_, { id }) => ({
name: 'Alice',
email: 'alice@example.com',
age: 25,
}),
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});在这个示例中,我们定义了一个 User 类型,它包含 name、email 和 age 三个字段。在 Query 类型中,我们定义了一个 user 字段,它接受一个 id 参数,并且返回一个 User 类型的对象。在 resolvers 中,我们实现了 user 字段的解析器,它返回了一个包含 name、email 和 age 三个字段的对象。
客户端代码
// www.javascriptcn.com code example
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000/',
cache: new InMemoryCache(),
});
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
name
email
age
}
}
`;
client.query({ query: GET_USER, variables: { id: '1' } })
.then(result => console.log(result))
.catch(error => console.error(error));在这个示例中,我们使用 Apollo Client 发送了一个查询,请求了一个名为 user 的字段,它接受一个 id 参数,并且返回一个包含 name、email 和 age 三个字段的对象。在 variables 中,我们传递了一个 id 参数,它的值为 '1'。
如果我们将查询中的 age 字段改为其他不存在的字段,例如 foo,服务端就会返回一个类型注释错误,告诉我们这个字段不存在:
// www.javascriptcn.com code example
{
"errors": [
{
"message": "Cannot query field 'foo' on type 'User'.",
"locations": [
{
"line": 5,
"column": 7
}
]
}
]
}在这种情况下,我们需要修改查询,确保它符合服务端的定义。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d4eed7a941bf713492d2f5