GraphQL cheat sheet
A scannable GraphQL reference: 15 short snippets across 8 topics, each linking back to the lesson it came from.
At a glance
| Topic | What it covers | |
|---|---|---|
| Writing queries: variables, fragments and directives | Named operations, variables with defaults, fragments on types and interfaces, aliases, and conditional inclusion with | lesson |
| Pagination and the connection pattern | Returning a bare list without a limit is the most common GraphQL production incident: one client asks for everything | lesson |
| Error handling, validation and custom scalars | Validate before touching the database, and validate again in the data layer. Schema validation proves the shape; it | lesson |
| DataLoader and solving the N+1 problem | The resolver has no way to know it is one of fifty. DataLoader solves it by deferring resolution to the end of the | lesson |
| Subscriptions and real-time updates | Treat a subscription as an optimisation, never as the only source of truth. The client should be able to reconstruct | lesson |
| Schema design, evolution and tooling | Generate types from the deployed schema, not from a copy in the repository, or the drift you were trying to prevent | lesson |
| Testing GraphQL APIs | Resolver unit tests, integration tests that execute a real document, schema snapshots, mocking data sources and testing | lesson |
| Federation, caching and next steps | Every request is a POST to one URL, so CDNs and browsers cannot use the URL as a cache key. Caching has to move to two | lesson |
Quick snippets
Writing queries: variables, fragments and directives
Variables instead of string building
{
"authorId": "42",
"first": 5,
"includeReviews": true
}Full lesson: Writing queries: variables, fragments and directives →
Pagination and the connection pattern
The connection shape
query Page($first: Int!, $after: String) {
books(first: $first, after: $after) {
nodes { id title }
pageInfo { hasNextPage endCursor }
}
}Full lesson: Pagination and the connection pattern →
Error handling, validation and custom scalars
A model clients can code against
{
"data": {
"placeOrder": {
"order": null,
"errors": [
{ "field": "couponCode", "code": "COUPON_EXPIRED", "message": "This coupon expired on 1 June." }
]
}
}
}
Input validation
input CreateBookInput {
title: String!
isbn: String!
pageCount: Int!
publishedOn: Date
}
scalar Date
scalar DateTime
scalar URLFull lesson: Error handling, validation and custom scalars →
DataLoader and solving the N+1 problem
The shape of the problem
query {
books(first: 50) {
nodes {
id
title
author { name }
}
}
}
The shape of the problem
-- 1 query for the list
select * from book order by created_at desc limit 50;
-- then 50 more, because Book.author resolves once per node
select * from author where id = 1;
select * from author where id = 1;
select * from author where id = 7;
-- ... 48 more
Creating and using loaders
Mutation: {
async updateAuthor(_p, { id, name }, ctx) {
const updated = await ctx.db.author.update({ where: { id }, data: { name } });
ctx.loaders.authorById.clear(id); // otherwise the stale value is served
return updated;
},
}Full lesson: DataLoader and solving the N+1 problem →
Subscriptions and real-time updates
Defining a subscription
type Subscription {
bookPublished(authorId: ID!): Book!
orderStatusChanged(orderId: ID!): OrderStatusEvent!
}
type OrderStatusEvent {
orderId: ID!
status: OrderStatus!
changedAt: DateTime!
}
Delivery and reliability
import { RedisPubSub } from "graphql-redis-subscriptions";
import Redis from "ioredis";
const pubsub = new RedisPubSub({
publisher: new Redis(process.env.REDIS_URL),
subscriber: new Redis(process.env.REDIS_URL),
});
// a client that reconnects must catch up
// query the delta since lastEventId before resuming the subscription
const { data } = await client.query({ query: ORDER_EVENTS_SINCE, variables: { since: lastEventId } });
Running subscriptions in production
// a graceful shutdown that closes sockets properly
process.on("SIGTERM", async () => {
await server.stop(); // stop accepting new connections
await pubsub.close(); // release the redis subscribers
await db.destroy();
process.exit(0);
});Full lesson: Subscriptions and real-time updates →
Schema design, evolution and tooling
Changing a schema without breaking clients
type Book {
id: ID!
title: String!
publishedOn: Date @deprecated(reason: "Use publishedAt, which includes a timezone.")
publishedAt: DateTime
}
type Query {
books(first: Int): BookConnection!
allBooks: [Book!]! @deprecated(reason: "Unbounded. Use books with pagination.")
}
Code generation and CI checks
# codegen.yml
schema: http://localhost:4000/graphql
documents: "src/**/*.graphql"
generates:
src/generated/types.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
Code generation and CI checks
# in CI: fail the build on a breaking change
npx graphql-inspector diff origin/main:schema.graphql schema.graphql --rule suppressRemovalOfDeprecatedField
npx graphql-inspector validate "src/**/*.graphql" schema.graphql
npx eslint src --ext .tsFull lesson: Schema design, evolution and tooling →
Testing GraphQL APIs
Schema snapshots and mocking
import { printSchema, lexicographicSortSchema } from "graphql";
it("matches the published schema", () => {
const current = printSchema(lexicographicSortSchema(schema));
expect(current).toMatchSnapshot();
});
// mock a data source so resolver tests stay fast and deterministic
const fakeBookRepo = {
findPage: vi.fn().mockResolvedValue({ nodes: [{ id: "1", title: "A", authorId: "42" }] }),
findManyByIds: vi.fn().mockResolvedValue([{ id: "42", name: "Le Guin" }]),
};Full lesson: Testing GraphQL APIs →
Federation, caching and next steps
Where to go next
query Dashboard {
summary { orderCount revenue }
... @defer {
recentOrders { nodes { id total } }
}
}Full lesson: Federation, caching and next steps →
FAQ
Is this GraphQL cheat sheet free to use?
Where do the examples come from?
How do I go deeper than a cheat sheet?
Related cheat sheets
Last refreshed 2026-09-27.