Copy-ready GraphQL patterns for common use cases. Each example includes a query and explanation of when to use it.
Fetch a single resource by ID. The simplest possible query — request specific fields and nothing more.
{
user(id: "42") {
id
name
email
}
}
Request related data in a single round-trip. Here, we fetch a user and their recent posts along with each post's comment count.
query UserWithPosts($userId: ID!) {
user(id: $userId) {
name
posts(first: 10) {
edges {
node {
title
publishedAt
commentCount
}
}
}
}
}
Create a new resource using an input type. The mutation returns the created object so the client can update its local cache.
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
body
published
createdAt
}
}
# Variables
# {
# "input": {
# "title": "Cursor Pagination in Practice",
# "body": "Cursor-based pagination provides...",
# "tags": ["graphql", "pagination"],
# "published": true
# }
# }
Listen for real-time events over WebSocket. The server pushes data to the client whenever the specified event occurs.
subscription OnCommentAdded($postId: ID!) {
commentAdded(postId: $postId) {
id
text
author {
name
avatar
}
createdAt
}
}
The Relay connection specification. Use first/after for forward pagination, last/before for backward. Cursors are opaque strings — treat them as tokens, not offsets.
query PaginatedPosts($first: Int!, $after: String) {
posts(first: $first, after: $after) {
edges {
cursor
node {
id
title
excerpt
author { name }
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
Pass filter arguments to narrow results. Use an input type to group filter parameters. This keeps the schema organized and makes queries readable.
query FilteredPosts($filter: PostFilter) {
posts(filter: $filter, first: 20) {
edges {
node {
id
title
tags
publishedAt
}
}
}
}
# Variables
# {
# "filter": {
# "tags": ["graphql"],
# "publishedAfter": "2024-01-01",
# "status": "PUBLISHED"
# }
# }
Pass a bearer token in the HTTP headers. The server extracts the token, verifies it, and attaches the authenticated user to the GraphQL context.
# Headers
{
"Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6...",
"Content-Type": "application/json"
}
# Query
query Me {
me {
id
name
email
role
permissions
}
}
GraphQL responses include partial data alongside errors. The extensions field carries structured error metadata such as codes that clients can handle programmatically.
{
"data": {
"updatePost": null
},
"errors": [
{
"message": "Not authorized to update this post",
"path": ["updatePost"],
"extensions": {
"code": "FORBIDDEN",
"timestamp": "2025-01-20T14:32:00Z"
}
}
]
}
Reuse field selections across multiple queries. Fragments reduce duplication and keep queries maintainable as the schema grows.
fragment PostFields on Post {
id
title
excerpt
publishedAt
author {
name
}
}
query Dashboard {
recentPosts(first: 5) {
...PostFields
}
popularPosts(first: 5) {
...PostFields
viewCount
}
}
Execute multiple operations of the same type in a single request using aliases. Each alias maps to a separate resolver call.
mutation BatchUpdate {
first: updatePostStatus(id: "101", status: PUBLISHED) {
id
status
}
second: updatePostStatus(id: "102", status: PUBLISHED) {
id
status
}
third: updatePostStatus(id: "103", status: ARCHIVED) {
id
status
}
}