GraphQL - Query
GraphQL - 查询
Section titled “GraphQL - 查询”在 GraphQL 中,查询(Query)是用于从服务器读取或获取数据的操作。它类似于 REST 中的 GET 请求。客户端构造查询时,精确指定他们需要的字段(Field),可能包括嵌套的相关数据以及用于过滤或标识特定对象的参数(Argument)。
服务器处理查询,执行必要的解析器(Resolvers),并返回一个与请求字段结构精确匹配的 JSON 响应。
基本查询语法
Section titled “基本查询语法”最简单的查询是请求模式(Schema)的 Query 类型中定义的字段:
# 语法 1:匿名查询(简写){ fieldName}
# 语法 2:命名查询(推荐用于清晰度和多个操作)query OperationName { fieldName}示例(假设 Query 类型中存在 greeting 字段):
# 匿名查询{ greeting}
# 命名查询query GetGreeting { greeting}尽管对于单个查询来说,query 关键字和操作名称是可选的,但显式命名您的操作是一种最佳实践,尤其是在处理变量(Variables)或在一次请求中包含多个操作时。
请求特定字段
Section titled “请求特定字段”GraphQL 的核心优势之一是避免过度获取(over-fetching)。对于给定的对象类型,您只列出您需要的字段。
# 模式(Schema)片段# type Student {# id: ID!# firstName: String# lastName: String# email: String# collegeId: String# }# type Query {# students: [Student]# }
# 请求所有学生只包含 ID 和 firstName 的查询query GetStudentNames { students { id firstName # lastName 和 email 未被请求 }}响应将只包含一个对象数组,每个对象都包含 id 和 firstName。
字段可以接受参数(Arguments)来过滤数据或标识特定对象。
# 模式(Schema)片段# type Query {# studentById(id: ID!): Student# }
# 请求具有特定 ID 的学生的查询query GetSpecificStudent { studentById(id: "S1001") { # 传递参数 'id',值为 "S1001" id firstName lastName email }}如果您需要在单个操作中多次查询同一个字段并使用不同的参数,则必须使用别名(Aliases)来避免响应 JSON 中的命名冲突。
# 使用别名请求两个不同学生的查询query GetMultipleStudents { student1001: studentById(id: "S1001") { # 别名 'student1001' id firstName } student1002: studentById(id: "S1002") { # 别名 'student1002' id firstName }}
# 响应结构# {# "data": {# "student1001": { ... },# "student1002": { ... }# }# }片段(Fragments)是可重用的字段单元。当您需要在同一类型的多个对象上使用同一组字段时,它们有助于分解复杂查询或避免重复。
# 在 Student 类型上定义一个片段fragment StudentCoreDetails on Student { id firstName lastName}
# 在查询中使用片段query GetStudentsWithCoreDetails { student1001: studentById(id: "S1001") { ...StudentCoreDetails # 在此处展开片段字段 email # 请求查询此部分特有的附加字段 } allStudents: students { ...StudentCoreDetails # 在此处重用片段 }}直接将参数硬编码到查询字符串中不够灵活。变量(Variables)允许您在查询之外传递动态值。这使得查询可重用,并且对于安全性至关重要(防止注入问题)。
使用变量涉及三个步骤:
- 在查询定义中使用
$前缀声明变量,并指定其类型(例如,$studentId: ID!)。 - 在查询字段中使用变量(例如,
studentById(id: $studentId))。 - 在查询字符串之外发送一个单独的 JSON 字典,将变量名映射到其值。
# 1 & 2:使用变量定义的查询query GetStudentWithVariable($studentId: ID!) { studentById(id: $studentId) { id firstName lastName }}# 3:随请求发送的单独的变量 JSON{ "studentId": "S1002"}GraphQL 客户端(如 Apollo Client,或 Apollo Sandbox/GraphiQL 等工具)提供了发送查询字符串和变量对象两种数据的方式。
指令(Directives)提供了一种根据变量条件性地包含或跳过字段或片段的方式。常见的内置指令有 @include(if: Boolean!) 和 @skip(if: Boolean!)。
# 使用指令的条件字段查询query GetStudentWithOptionalEmail($studentId: ID!, $includeEmail: Boolean!) { studentById(id: $studentId) { id firstName email @include(if: $includeEmail) # 仅当 $includeEmail 为 true 时包含 email collegeId @skip(if: $includeEmail) # 如果 $includeEmail 为 true 则跳过 collegeId }}# 变量 JSON 示例 1 (includeEmail = true){ "studentId": "S1001", "includeEmail": true}# -> 响应将包含 'email',跳过 'collegeId'
# 变量 JSON 示例 2 (includeEmail = false){ "studentId": "S1001", "includeEmail": false}# -> 响应将跳过 'email',包含 'collegeId'示例 1:使用自定义字段解析器的查询
Section titled “示例 1:使用自定义字段解析器的查询”有时,您想要查询的字段并不直接存在于您的数据库模型中,但可以从其他字段计算得出。您可以使用该字段的特定解析器(Resolver)来处理这种情况。
目标:为学生查询一个 fullName 字段,该字段由 firstName 和 lastName 派生而来。
步骤 1:模式(Schema)修改
Section titled “步骤 1:模式(Schema)修改”将 fullName 字段添加到您的模式(Schema)中的 Student 类型:
# 在 schema.graphql 或 server.js 中type Student { id: ID! firstName: String lastName: String fullName: String # 计算得出的字段 # ... 其他字段}步骤 2:为自定义字段添加解析器
Section titled “步骤 2:为自定义字段添加解析器”在您的 resolvers.js(或等效文件)中,在 Student 类型的解析器映射内部专门为 fullName 字段添加一个解析器:
// 在 resolvers.js 中const resolvers = { Query: { // ... 您的 Query 解析器(例如 students, studentById) students: () => db.students.list(), studentById: (_, { id }) => db.students.get(id), },
Student: { // Student 类型内部字段的解析器 fullName: (parent, args, context, info) => { // 这里的 'parent' 是由 Query 解析器返回的学生对象 // 从父对象访问 firstName 和 lastName console.log(`Resolving Student.fullName for student ID: ${parent.id}`); return `${parent.firstName} ${parent.lastName}`; }, // 如果 id, firstName, lastName 直接可用,则无需为其定义解析器 // 在 Query 解析器返回的父对象上。 },};当您查询 fullName 时,GraphQL 会首先使用 Query.students 或 Query.studentById 解析器解析父级 Student 对象,然后执行 Student.fullName 解析器,将解析后的学生对象作为 parent 参数传递。
步骤 3:测试查询
Section titled “步骤 3:测试查询”运行您的服务器并使用 Apollo Sandbox 执行一个请求 fullName 的查询:
query GetStudentFullNames { students { id fullName # 请求计算字段 }}响应应包含组合后的全名。
示例 2:嵌套查询(关系)
Section titled “示例 2:嵌套查询(关系)”GraphQL 在一次请求中获取相关数据方面表现出色。
目标:获取学生详细信息以及他们关联的学院信息。
步骤 1:模式(Schema)修改
Section titled “步骤 1:模式(Schema)修改”确保您的模式(Schema)定义了 College 类型并从 Student 类型链接了它:
# 在 schema.graphql 或 server.js 中type College { id: ID! name: String location: String}
type Student { id: ID! firstName: String lastName: String collegeId: String # 外键(数据可能这样存储) college: College # 表示关系的字段}
type Query { students: [Student] # ... 其他查询}步骤 2:为关系字段添加解析器
Section titled “步骤 2:为关系字段添加解析器”为 Student.college 字段添加一个解析器。这个解析器将使用父学生对象中的信息(如 collegeId)来获取相关的学院数据。
// 在 resolvers.js 中// 假设您的模拟 db.js 中存在 db.colleges.get(id)
const resolvers = { Query: { // ... Query 解析器 students: () => db.students.list(), }, Student: { // ... 其他 Student 解析器,如 fullName
// 'college' 关系字段的解析器 college: (parent, args, context, info) => { // 'parent' 是学生对象 console.log(`Resolving Student.college for student ID: ${parent.id}, collegeId: ${parent.collegeId}`); // 使用父学生对象的 collegeId 来获取学院 return db.colleges.get(parent.collegeId); }, }, // 如果需要,可能需要为 College 字段添加解析器};这个解析器会为 Query.students 返回的每个学生运行。它从学生对象(parent.collegeId)中获取 collegeId,并使用它在(模拟的)学院数据源中查找相应的学院。
步骤 3:测试嵌套查询
Section titled “步骤 3:测试嵌套查询”运行您的服务器并在 Apollo Sandbox 中执行嵌套查询:
query GetStudentsAndColleges { students { id firstName college { # 查询关系字段 # 请求嵌套 College 对象中的字段 id name location } }}响应将包含一个学生数组,每个学生都有一个嵌套的 college 对象,其中包含请求的学院详细信息,所有这些都在一次对服务器的请求中完成。