查询变更与订阅三种操作


查询变更与订阅三种操作

Query:只读数据获取

GraphQL的所有操作分为三种类型,其中Query是最常用的——它是只读的数据获取操作。每个Query操作对应Schema中 type Query 里定义的一个入口字段。

type Query { # 基础查询 user(id: ID!): User post(id: ID!): Post # 列表查询,带分页参数 posts(limit: Int = 10, offset: Int = 0): [Post!]! # 关系遍历 userFriends(userId: ID!): [User!]! # 搜索 search(query: String!, category: SearchCategory): [SearchResult!]! } ​

一个Query操作的完整写法:

query GetUserProfile($userId: ID!, $postLimit: Int) { user(id: $userId) { id name email avatarUrl posts(limit: $postLimit) { id title createdAt likesCount comments(first: 3) { body author { name avatarUrl } } } friendsCount } } ​

操作名 GetUserProfile 是可选的,但推荐写上——它出现在日志和监控中,方便调试。变量声明 ($userId: ID!, $postLimit: Int) 在查询体内作为参数引用,避免了字符串拼接的安全风险和可读性问题。

query GetPaginatedPosts($limit: Int = 20, $cursor: String) { posts(first: $limit, after: $cursor) { edges { node { id title author { name } } cursor } pageInfo { hasNextPage endCursor } } } ​

这段查询展示了基于游标的分页模式,这是GraphQL中推荐的分页方式。每条数据包含一个 cursor,翻页时把上一页最后一条的 cursor 传给 after 参数。

Mutation:数据修改

Mutation用于创建、更新、删除数据。它的执行有一个关键特性:顺序执行。GraphQL规范规定同一个Mutation操作内的多个字段按书写顺序依次执行,这避免了并发写入的竞态条件。

type Mutation { createPost(input: CreatePostInput!): CreatePostPayload! updatePost(id: ID!, input: UpdatePostInput!): UpdatePostPayload! deletePost(id: ID!): DeletePostPayload! toggleLike(postId: ID!): ToggleLikePayload! addComment(input: AddCommentInput!): AddCommentPayload! } ​

一个创建帖子的Mutation:

mutation CreateNewPost($input: CreatePostInput!) { createPost(input: $input) { post { id title content createdAt } success errors { field message } } } ​

变量值通过JSON传入:

{ "input": { "title": "GraphQL学习笔记", "content": "今天学习了Schema...", "tags": ["graphql", "tutorial"] } } ​

Payload模式(CreatePostPayload)是Mutation返回值的好实践。它不仅包含创建的实体,还包含操作状态(success)和可能的错误信息(errors),让客户端能清晰地处理成功和失败两种情况。

type CreatePostPayload { post: Post success: Boolean! errors: [MutationError] } type MutationError { field: String message: String! } ​

多个Mutation可以组合在一个操作中,它们会按顺序执行:

mutation LikeAndComment($postId: ID!, $commentBody: String!) { # 先点赞 toggleLike(postId: $postId) { post { id likesCount } } # 再评论 addComment(input: { postId: $postId, body: $commentBody }) { comment { id body createdAt } } } ​

这种顺序执行的保证是REST做不到的——如果你用REST发两个并行请求(一个点赞、一个评论),它们的执行顺序是不确定的。

Subscription:实时数据推送

Subscription是GraphQL对实时数据推送的标准化方案。它基于WebSocket连接(或Server-Sent Events),客户端订阅某个事件,当事件触发时,服务器推送数据给客户端。

type Subscription { postLiked(postId: ID!): LikeEvent! newComment(postId: ID!): Comment! userOnline(userId: ID!): User! orderStatusChanged(orderId: ID!): OrderStatusUpdate! } ​

客户端订阅代码示例(使用Apollo Client):

subscription OnPostLiked($postId: ID!) { postLiked(postId: $postId) { user { id name avatarUrl } post { id likesCount } timestamp } } ​

当有人点赞了指定帖子时,服务器会通过WebSocket推送一条消息:

{ "data": { "postLiked": { "user": { "id": "42", "name": "张三", "avatarUrl": "..." }, "post": { "id": "1001", "likesCount": 43 }, "timestamp": "2026-01-15T10:30:00Z" } } } ​

在服务端实现中,Subscription使用发布/订阅模式。解析器不是直接返回数据,而是返回一个异步迭代器(AsyncIterator),当事件触发时yield数据:

// 服务端 Subscription 解析器 const resolvers = { Subscription: { postLiked: { subscribe: (_, { postId }, context) => { return context.pubsub.asyncIterator(`POST_LIKED:${postId}`); }, resolve: (payload) => payload } }, Mutation: { toggleLike: async (_, { postId }, context) => { // ... 点赞逻辑 await context.pubsub.publish(`POST_LIKED:${postId}`, likeEvent); return likeEvent; } } }; ​

三种操作的生命周期对比

三种操作共享同一个Schema、同一种类型系统、同一个 /graphql 端点。区别在于执行语义:Query是幂等的只读操作,Mutation是可能有副作用的写操作且保证顺序执行,Subscription是建立持久连接的事件推送。

图:GraphQL操作类型对比

图:GraphQL操作类型对比

在实际项目中,一个操作应该是Query还是Mutation的判断标准是:是否有副作用。如果操作会修改服务端状态,用Mutation;否则用Query。Subscription的使用场景更明确:需要实时推送的数据(实时通知、在线协作、IoT设备状态等)。

作者与出处
原作者:灏天文库
来源:平台策划编纂
整理人: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U