前言
参考文章: https://chenyitian.gitbooks.io/graphql/content/introduction.html
参考网站: https://www.howtographql.com/basics/0-introduction/
GraphQL基础
1. 什么是GraphQL
GraphQL是一种用于API的查询语言,同时也是一种运行在服务端的执行引擎。它使用基于类型系统的模式定义和验证查询结构(类型系统由你的数据定义)。GraphQL不依赖任何特定数据库或存储引擎,而是通过现有代码和数据来支持查询
为什么使用GraphQL
- 灵活的数据获取: 客户端可以请求确切的数据结构,避免数据过多或不足的问题
- 单一端点: 所有查询通过一个端点完成,简化了客户端与服务端的交互
- 强类型系统: 定义
API时使用强类型,可以捕获开发错误,增强工具支持(如自动生成文档和代码) - 实时更新: 通过订阅(
subscriptions),支持实时数据更新,客户端在数据变化时能自动接收更新
与REST的对比
优点
- 避免多次请求: 例如获取用户和其文章,
REST需要GET /users/1和GET /users/1/posts两次请求,GraphQL只需一次查询同时获取用户和文章数据 - 精确的数据获取: 客户端明确指定需要的字段 (如只要
name和age,不要email),避免多余数据传输,特别适用于移动端或弱网环境
缺点
- 查询解析复杂: 服务器需要解析客户端自定义的查询结构、验证类型、执行深度检查,比
REST固定端点的处理逻辑更复杂 - 缓存困难:
REST使用URL作为缓存键 (如GET /users/1),而GraphQL所有请求都是POST /graphql,查询内容在请求体中,无法利用标准HTTP缓存,需要使用 Apollo Client 等工具自行实现缓存策略 - 学习曲线: 需要掌握 Schema 设计、类型系统、Resolver 实现、N+1 查询问题等新概念,比
REST的学习成本更高
2. 字段Fields
在GraphQL中,字段 (fields) 是定义在类型 (type) 上的属性,表示类型包含的数据内容。字段有以下特点
- 每个字段都有名称和返回类型,类型可以是标量(如
String、Int)、对象、枚举、列表等 - 字段可以接受参数,用于传递附加信息或过滤数据
字段的基本定义
type User { id: ID! name: String! age: Int }
type Query { user(id: ID!): User }
|
User类型: 定义用户数据结构,包含id、name、age三个字段,其中!表示字段为必需(非空)Query类型: 定义查询操作user,通过必需参数id查询并返回对应的User对象
客户端查询示例
query { user(id: "1") { id name age } }
|
- 客户端传入参数
id: "1",请求该用户的id、name和age字段 - 服务器返回对应用户的数据
什么是Schema
Schema是GraphQL的核心概念,用于描述API的结构。它定义了客户端可以查询的所有类型和操作
# 定义用户类型 type User { id: ID! name: String! age: Int }
# 定义查询操作 type Query { user(id: ID!): User users: [User] }
# 定义变更操作 type Mutation { createUser(name: String!, age: Int!): User }
# Schema 将所有类型组合起来 schema { query: Query mutation: Mutation }
|
Schema的组成部分- 类型定义
Type: 定义数据结构,如User类型包含id、name、age字段 - 根类型
Root Types: Query(查询)、Mutation(变更)、Subscription(订阅) schema声明: 指定哪些类型作为入口点(通常可以省略,默认使用Query、Mutation、Subscription)
解析器Resolver
- 解析器定义了如何获取
Schema中每个字段的实际数据。当客户端请求某个字段时,GraphQL会调用对应的解析器
const users = [ { id: '1', name: 'Alice', age: 30 }, { id: '2', name: 'Bob', age: 25 }, ];
const resolvers = { Query: { user: (parent, args, context, info) => { return users.find(user => user.id === args.id); }, users: () => { return users; }, }, };
|
- 解析器函数接收 4 个参数
parent: 上一级解析器的返回值(嵌套查询时使用)args: 客户端传入的参数对象(如{ id: "1" })context: 所有解析器共享的上下文对象(如数据库连接、用户认证信息)info: 当前查询的元数据信息(字段名、路径等)
3. 参数Arguments
- 在
GraphQL中,参数 (Arguments) 用于向字段或操作(如查询Query和变更Mutation)传递输入数据。参数允许客户端在请求中提供额外信息,用于筛选、过滤或指定要操作的数据
参数的定义
- 参数可以定义在
GraphQL schema中的字段上,分为必需参数(使用!符号)和可选参数(无!符号)
type User { id: ID! name: String! age: Int }
type Query { user(id: ID!): User users(age: Int): [User] }
|
- 代码说明
user(id: ID!): id是必需参数,必须提供才能查询users(age: Int): age是可选参数,可以用于筛选特定年龄的用户[User]: 方括号表示返回值是用户列表
解析器中的参数
- 在解析器 (
Resolver) 中,参数通过args对象传递,可以使用这些参数来筛选或操作数据
const resolvers = { Query: { user: (parent, args, context, info) => { return getUserById(args.id); }, users: (parent, args, context, info) => { if (args.age) { return getUsersByAge(args.age); } return getAllUsers(); }, }, };
|
- 参数使用说明
args.id / args.age: 客户端请求中传递的参数user查询: 通过id获取单个用户users查询: 根据age筛选用户列表,未提供age时返回所有用户
客户端查询
query { user(id: "1") { id name age } users(age: 30) { id name age } }
|
- 查询说明
user(id: "1"): 请求id为"1"的单个用户users(age: 30): 请求所有age为30的用户列表
Mutation中的参数
# Schema ----------------- type Mutation { createUser(name: String!, age: Int): User }
type User { id: ID! name: String! age: Int }
# Resolver (JavaScript) --- const resolvers = { Mutation: { createUser: (parent, args, context, info) => { const newUser = { id: generateUniqueId(), name: args.name, age: args.age, }; saveUserToDatabase(newUser); return newUser; }, }, };
# Client Request ---------- mutation { createUser(name: "Alice", age: 30) { id name age } }
|
- 执行流程说明
- 客户端传递
name和可选age参数到createUser - 解析器生成包含唯一
id的新用户对象 - 保存用户数据到持久化存储
- 返回新增用户的完整数据(
id、name、age)
GraphQL核心功能
4. 别名Aliases
避免命名冲突
- 为每次对同一字段的不同参数请求添加不同别名,避免响应中键名重复
query { user1: user(id: "1") { id name age } user2: user(id: "2") { id name age } }
|
- 示例说明
user1: user(id: "1"): 使用别名user1标识第一条user查询user2: user(id: "2"): 使用别名user2标识第二条user查询- 服务端仍调用同一个
user解析器两次,客户端通过不同键名接收结果,无命名冲突
定义响应格式
- 别名可以改变返回数据中的字段名称,使其更符合业务需求或提高可读性
{ "data": { "user1": { "id": "1", "name": "Alice", "age": 30 }, "user2": { "id": "2", "name": "Bob", "age": 25 } } }
|
user1和user2成为响应数据中的字段名称,分别包含两个不同用户的信息
5. 片段Fragments
- 片段用于复用一组字段选择,避免在多个查询/变更/订阅中重复书写相同字段,提升可读性与维护性
- 使用价值
- 减少重复: 一处定义,多处引用
- 保持一致: 保证不同查询返回字段结构一致
- 易于修改: 修改片段即可同步更新所有使用位置
片段的语法
fragment FragmentName on TypeName { field1 field2 ... }
|
- 语法说明
FragmentName: 片段名称,后续通过...FragmentName引用on TypeName: 指定该片段绑定的类型(只能在该类型或其实现处使用)- 片段体: 需要复用的字段集合
片段的使用示例
- 场景: 多次查询
User不同实例但需要相同字段集合 - 示例代码(片段定义 -> 查询使用 -> 返回结果)
# Fragment Definition ----------------- fragment UserFields on User { id name age }
# Query Using Fragment ---------------- query { user1: user(id: "1") { ...UserFields } user2: user(id: "2") { ...UserFields } }
# Result JSON ------------------------- { "data": { "user1": { "id": "1", "name": "Alice", "age": 30 }, "user2": { "id": "2", "name": "Bob", "age": 25 } } }
|
嵌套片段
- 通过在一个片段内部引用另一个片段,实现复杂对象字段的分层复用
- 适用场景: 对象包含子对象,需要分别维护子对象字段集合
- 示例代码(片段定义 -> 查询使用 -> 返回结果)
# Fragment Definitions ----------------- fragment AddressFields on Address { street city country }
fragment UserFields on User { id name age address { ...AddressFields } }
# Query Using Nested Fragment ---------- query { user(id: "1") { ...UserFields } }
# Result JSON -------------------------- { "data": { "user": { "id": "1", "name": "Alice", "age": 30, "address": { "street": "123 Main St", "city": "New York", "country": "USA" } } } }
|
内联片段
- 用途: 针对接口或联合类型中不同具体实现返回不同字段集合
- 使用场景
- 接口/联合类型多态数据
- 需要根据实际类型选择附加字段
- 语法:
... on TypeName { fields } - 示例代码(语法 -> Schema -> 查询使用 -> 返回结果)
# Inline Fragment Syntax --------------- ... on TypeName { field1 field2 }
# Schema -------------------------------- interface Person { id: ID! name: String! }
type Employee implements Person { id: ID! name: String! salary: Int }
type Customer implements Person { id: ID! name: String! purchaseHistory: [String] }
type Query { persons: [Person] }
# Query Using Inline Fragments --------- query { persons { id name ... on Employee { salary } ... on Customer { purchaseHistory } } }
# Result JSON -------------------------- { "data": { "persons": [ { "id": "1", "name": "Alice", "salary": 50000 }, { "id": "2", "name": "Bob", "purchaseHistory": ["item1", "item2"] } ] } }
|
6. 操作名称
在GraphQL中,操作名称 (Operation Name) 是一个可选的标识,用于明确标注特定的查询 (Query)、变更 (Mutation) 或订阅 (Subscription) 操作的名称。它有助于提高可读性、调试效率,以及在某些工具中实现特定功能
操作名称特点
- 操作名称的位置
- 紧跟在操作类型(如
query、mutation或subscription)之后
- 命名规则
- 名称只能包含字母、数字和下划线
(_) - 名称不能以数字开头
- 用途
- 提供查询的上下文,明确查询意图
- 在调试、日志记录和工具(如
Apollo Client)中,用于识别特定的操作 - 当一个请求中包含多个操作时,必须使用操作名称
操作名称示例
- 查询示例:
GetUser是操作名称,用于标识此查询操作
query GetUser { user(id: "1") { id name age } }
|
- 变更示例:
CreateUser是操作名称,表明此变更的目的为创建用户
mutation CreateUser { createUser(name: "Alice", age: 30) { id name age } }
|
- 多操作示例: 当一个请求中包含多个操作时,必须使用操作名称进行区分
query GetUser { user(id: "1") { id name age } }
mutation UpdateUser { updateUser(id: "1", name: "Alice") { id name age } }
|
- 匿名操作: 如果操作名称被省略,该操作称为匿名操作, 但在一个请求中不能包含多个匿名操作,因为没有操作名称用于区分
query { user(id: "1") { id name age } }
|
7. 操作类型
在GraphQL中,操作类型 (Operation Type) 是指客户端可以在API请求中执行的三种操作: 查询 (Query)、变更 (Mutation) 和 订阅 (Subscription)。每种操作类型对应不同的用途,分别用于读取数据、修改数据和监听实时数据更新
操作类型介绍
- 查询
Query- 用途: 用于读取和获取数据
- 特点: 不会对服务器数据产生任何修改,仅返回请求的数据 (类似于
REST API的GET请求)
query { user(id: "1") { id name age } }
<!-- 查询 user 数据,根据参数 id: "1" 获取特定用户的信息 --> <!-- 返回的字段包括 id、name 和 age -->
|
- 变更
Mutation- 用途: 用于修改服务器上的数据,包括创建、更新或删除操作
- 特点: 与查询不同,变更操作会改变服务器的数据状态 (类似于
REST API的POST、PUT、PATCH和 DELETE请求)
mutation { createUser(name: "Alice", age: 30) { id name } }
<!-- 调用 createUser 变更操作,创建一个新用户 --> <!-- 参数 name 和 age 提供新用户的基本信息 --> <!-- 返回结果包括新用户的 id 和 name -->
|
- 订阅
Subscription- 用途: 用于监听服务器上的事件,并在事件发生时实时接收更新
- 特点: 用于实时数据更新场景,如实时聊天、股票行情等。订阅操作会保持一个持久的连接(通常使用
WebSocket),当事件发生时,服务器会主动推送更新给客户端
subscription { userAdded { id name age } }
<!-- 订阅 userAdded 事件,当有新用户被添加时,服务器会推送更新数据 --> <!-- 返回结果包含新用户的 id、name 和 age -->
|
三种操作类型的比较
| 操作类型 | 用途 | 是否修改数据 | 使用场景 |
|---|
| 查询 | 读取和获取数据 | 否 | 获取用户、产品列表、详情页数据 |
| 变更 | 修改服务器数据 | 是 | 创建、更新、删除资源 |
| 订阅 | 监听事件并实时接收数据更新 | 否 | 实时聊天、通知、数据流 |
8. 查询Query
查询 (Query) 是GraphQL中的一种操作类型,用于从服务器获取数据。它与传统REST API的GET请求类似,但在灵活性和强大性方面超越了后者
查询的定义
- 在
GraphQL中,查询类型通常在schema中定义为Query类型。Query类型描述了所有可执行的查询操作
type Query { user(id: ID!): User users: [User] }
type User { id: ID! name: String! age: Int }
|
查询解析器Resolver
- 解析器负责实现字段的具体逻辑,定义如何获取数据。以下是查询解析器的示例
const users = [ { id: '1', name: 'Alice', age: 30 }, { id: '2', name: 'Bob', age: 25 }, ];
const resolvers = { Query: { user: (parent, args, context, info) => { return users.find(user => user.id === args.id); }, users: () => users, }, };
|
user: 根据传入的id参数,从users列表中查找并返回对应用户users: 直接返回所有用户
查询的特点
- 精确的数据获取: 客户端可以指定所需字段,避免传输多余数据
- 示例代码(精确字段查询 -> 多资源单请求 -> 各自结果)
# Query (Precise Fields) --------------- query { user(id: "1") { name age } }
# Result JSON (Precise Fields) --------- { "data": { "user": { "name": "Alice", "age": 30 } } }
# Query (Multiple Resources Single Request) query { user(id: "1") { id name } users { id name } }
# Result JSON (Multiple Resources) ----- { "data": { "user": { "id": "1", "name": "Alice" }, "users": [ { "id": "1", "name": "Alice" }, { "id": "2", "name": "Bob" } ] } }
|
9. 变更Mutations
变更 (Mutations) 是GraphQL中的一种操作类型,专门用于对服务器端的数据进行修改操作。与查询 (Query) 仅用于读取数据不同,变更支持创建、更新或删除数据。每个变更操作都可以定义所需的输入参数和返回结果的结构
变更的定义
- 在
GraphQL的schema中,变更类型通常定义在Mutation类型中,用来描述所有支持的数据修改操作
type Mutation { createUser(name: String!, age: Int!): User updateUser(id: ID!, name: String, age: Int): User deleteUser(id: ID!): Boolean }
type User { id: ID! name: String! age: Int }
|
createUser: 接收name和age参数,返回新创建的User对象updateUser: 接收id(必需)、name和age参数,用于更新用户信息,返回更新后的UserdeleteUser: 接收id参数,用于删除指定用户,返回布尔值表示操作是否成功
变更的解析器
- 解析器定义了如何执行变更操作,通常与数据库或其他数据存储交互
const { v4: uuidv4 } = require('uuid');
const users = [ { id: '1', name: 'Alice', age: 30 }, { id: '2', name: 'Bob', age: 25 }, ];
const resolvers = { Mutation: { createUser: (parent, args, context, info) => { const newUser = { id: uuidv4(), name: args.name, age: args.age, }; users.push(newUser); return newUser; }, updateUser: (parent, args, context, info) => { const user = users.find(user => user.id === args.id); if (!user) { throw new Error("User not found"); } if (args.name !== undefined) { user.name = args.name; } if (args.age !== undefined) { user.age = args.age; } return user; }, deleteUser: (parent, args, context, info) => { const userIndex = users.findIndex(user => user.id === args.id); if (userIndex === -1) { throw new Error("User not found"); } users.splice(userIndex, 1); return true; }, }, };
|
变更的使用
客户端可以通过以下查询发送变更请求
- 示例代码(创建用户 -> 更新用户 -> 删除用户 -> 各自结果)
# Create User Mutation ----------------- mutation { createUser(name: "Alice", age: 30) { id name age } }
# Result JSON (Create) ----------------- { "data": { "createUser": { "id": "c81e728d-59f2-4d16-9f02-fcad84756789", "name": "Alice", "age": 30 } } }
# Update User Mutation ----------------- mutation { updateUser(id: "1", name: "Alice Updated") { id name age } }
# Result JSON (Update) ----------------- { "data": { "updateUser": { "id": "1", "name": "Alice Updated", "age": 30 } } }
# Delete User Mutation ----------------- mutation { deleteUser(id: "2") }
# Result JSON (Delete) ----------------- { "data": { "deleteUser": true } }
|
10. 订阅Subscription
订阅 (Subscription) 是GraphQL中的一种操作类型,允许客户端订阅服务器上的事件,并在事件发生时实时接收更新数据。这种机制特别适合实时更新场景,如聊天消息、通知、股票价格等
订阅的工作原理
- 建立订阅连接: 客户端发送订阅请求到服务器,指定要订阅的事件或数据
- 保持连接: 服务器与客户端之间通过
WebSocket或其他双向通信协议维持持久连接 - 事件触发: 当服务器端的事件发生变化(如新增或更新数据)时,触发对应的订阅逻辑
- 数据推送: 服务器将更新的数据通过连接推送到订阅的客户端
订阅示例
type User { id: ID! name: String! age: Int }
type Subscription { userAdded: User }
type Mutation { createUser(name: String!, age: Int!): User }
|
const { PubSub } = require('graphql-subscriptions'); const pubsub = new PubSub();
const resolvers = { Subscription: { userAdded: { subscribe: () => pubsub.asyncIterator(['USER_ADDED']), }, }, Mutation: { createUser: (parent, args, context, info) => { const newUser = { id: generateUniqueId(), name: args.name, age: args.age, };
saveUserToDatabase(newUser);
pubsub.publish('USER_ADDED', { userAdded: newUser });
return newUser; }, }, };
|
subscription { userAdded { id name age } }
|
- 当有新用户通过
createUser变更操作添加时,服务器会触发userAdded事件,客户端将接收到用户信息
Apollo Client订阅
- 配置
Apollo Client: 创建一个支持查询、变更和订阅的客户端
import { ApolloClient, InMemoryCache, split } from '@apollo/client'; import { WebSocketLink } from '@apollo/client/link/ws'; import { HttpLink } from '@apollo/client'; import { getMainDefinition } from '@apollo/client/utilities';
const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql' });
const wsLink = new WebSocketLink({ uri: 'ws://localhost:4000/graphql', options: { reconnect: true, }, });
const splitLink = split( ({ query }) => { const definition = getMainDefinition(query); return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ); }, wsLink, httpLink, );
const client = new ApolloClient({ link: splitLink, cache: new InMemoryCache(), });
|
- 订阅操作: 使用
Apollo Client的useSubscription Hook实现订阅
import { gql, useSubscription } from '@apollo/client';
const USER_ADDED_SUBSCRIPTION = gql` subscription OnUserAdded { userAdded { id name age } } `;
function UserList() { const { data, loading, error } = useSubscription(USER_ADDED_SUBSCRIPTION);
if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error.message}</p>;
return ( <div> <h2>New User Added:</h2> <p>ID: {data.userAdded.id}</p> <p>Name: {data.userAdded.name}</p> <p>Age: {data.userAdded.age}</p> </div> ); }
export default UserList;
|
- 总而言之,
GraphQL的Subscription依赖于WebSocket来实现。WebSocket提供了双向持久连接的能力,使得服务器能够在事件发生时立即向客户端推送数据,而Subscription则定义了这些事件和数据的结构。因此,Subscription不是替代WebSocket,而是利用WebSocket来实现特定的功能和需求
GraphQL进阶
11. 变量 Variables
在GraphQL中,变量 (Variables) 是一种机制,用于动态传递参数到查询 (Query)、变更 (Mutation) 或订阅 (Subscription) 中。通过使用变量,可以使查询更加灵活、可重用,并且避免将用户输入直接嵌入到查询字符串中,从而提升安全性和可读性
变量的定义与使用
- 查询中的变量: 变量需要在查询语句中定义,并在执行查询时传入实际的值
query GetUser($id: ID!) { user(id: $id) { id name age } }
<!-- $id 是一个变量,表示用户的 ID --> <!-- 变量的类型为 ID!,! 表示这是一个必需字段 -->
|
- 变量在
React中的使用: 使用Apollo Client的useQuery Hook动态传递变量值
import { gql, useQuery } from '@apollo/client';
const GET_USER = gql` query GetUser($id: ID!) { user(id: $id) { id name age } } `;
function User({ userId }) { const { loading, error, data } = useQuery(GET_USER, { variables: { id: userId }, });
if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error.message}</p>;
return ( <div> <h2>User Details</h2> <p>ID: {data.user.id}</p> <p>Name: {data.user.name}</p> <p>Age: {data.user.age}</p> </div> ); }
export default User;
|
- 变更中的变量: 在变更操作中,变量同样可以动态传递参数
mutation CreateUser($name: String!, $age: Int!) { createUser(name: $name, age: $age) { id name age } }
|
import { gql, useMutation } from '@apollo/client';
const CREATE_USER = gql` mutation CreateUser($name: String!, $age: Int!) { createUser(name: $name, age: $age) { id name age } } `;
function CreateUserForm() { const [createUser, { data, loading, error }] = useMutation(CREATE_USER);
const handleSubmit = async (e) => { e.preventDefault(); const name = e.target.name.value; const age = parseInt(e.target.age.value, 10);
await createUser({ variables: { name, age } }); };
if (loading) return <p>Submitting...</p>; if (error) return <p>Error: {error.message}</p>;
return ( <form onSubmit={handleSubmit}> <input name="name" placeholder="Name" required /> <input name="age" placeholder="Age" type="number" required /> <button type="submit">Create User</button> {data && <p>Created User: {data.createUser.name}</p>} </form> ); }
export default CreateUserForm;
|
12. 指令 Directives
指令 (Directives) 是GraphQL中的一种强大功能,用于动态影响查询、变更 (Mutation) 或订阅 (Subscription) 的执行行为。指令可以应用于字段、片段等,帮助客户端或服务器灵活调整查询结果的生成逻辑。例如,可以根据条件决定是否包含某字段或动态修改返回结果
内置指令
GraphQL规范中定义了两个常用的内置指令: @include和@skip
@include: 条件包含- 用于根据条件动态包含字段或片段
- 接受一个名为
if的参数,该参数的值为布尔类型
query GetUser($withAge: Boolean!) { user(id: "1") { id name age @include(if: $withAge) } }
<!-- 如果变量 $withAge 为 true,则返回结果中包含 age 字段 --> <!-- 如果 $withAge 为 false,则排除 age 字段 -->
|
@skip: 条件跳过- 用于根据条件跳过字段或片段
- 同样接受一个名为
if的布尔参数
query GetUser($withoutAge: Boolean!) { user(id: "1") { id name age @skip(if: $withoutAge) } }
<!-- 如果变量 $withoutAge 为 true,则跳过 age 字段 --> <!-- 如果 $withoutAge 为 false,则包含 age 字段 -->
|
自定义指令
除了内置指令,GraphQL还支持自定义指令。这些指令可以用于更复杂的场景,比如数据验证、字段格式化等。自定义指令需要在schema中声明,并提供对应的实现逻辑
directive @upper on FIELD_DEFINITION
type Query { greeting: String @upper }
<!-- @upper 是一个自定义指令,应用在字段定义上 --> <!-- on FIELD_DEFINITION 指定了该指令适用于字段定义 -->
|
- 自定义指令的实现: 使用
graphql-tools创建一个@upper指令,将字符串字段的返回值转换为大写
const { SchemaDirectiveVisitor } = require('graphql-tools'); const { defaultFieldResolver } = require('graphql');
class UpperCaseDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { const { resolve = defaultFieldResolver } = field;
field.resolve = async function (...args) { const result = await resolve.apply(this, args); if (typeof result === 'string') { return result.toUpperCase(); } return result; }; } }
module.exports = UpperCaseDirective;
|
- 整合指令到
Schema: 自定义指令@upper应用于greeting字段,返回的值将自动转换为大写
const { gql } = require('apollo-server'); const UpperCaseDirective = require('./UpperCaseDirective');
const typeDefs = gql` directive @upper on FIELD_DEFINITION
type Query { greeting: String @upper } `;
const resolvers = { Query: { greeting: () => "hello world", }, };
const { makeExecutableSchema } = require('graphql-tools');
const schema = makeExecutableSchema({ typeDefs, resolvers, schemaDirectives: { upper: UpperCaseDirective, }, });
module.exports = schema;
|
{ "data": { "greeting": "HELLO WORLD" } }
|