GraphQL Cheat Sheet

Complete GraphQL reference covering SDL schema types, queries, mutations, subscriptions, resolvers, DataLoader, Apollo Client, pagination patterns, and performance best practices.

What is GraphQL?

GraphQL is a query language for APIs and a runtime for executing those queries developed by Facebook (now Meta) in 2012 and open-sourced in 2015. Unlike REST, where each endpoint returns a fixed data structure, GraphQL allows clients to request exactly the fields they need — no more, no less — in a single request.

The core idea is a strongly-typed schema that serves as a contract between client and server. Every piece of data your API can return is described in the schema using the Schema Definition Language (SDL). This schema drives auto-documentation, validation, and tooling like GraphiQL and Apollo Studio.

Key Features

  • ▸Declarative data fetching — clients specify the exact shape of data needed
  • ▸Single endpoint — all operations go through one URL (typically /graphql)
  • ▸Strongly typed schema — every field has a declared type; queries are validated before execution
  • ▸Introspection — clients can query the schema itself to discover available types and operations
  • ▸Real-time with subscriptions — WebSocket-based live updates built into the spec

When to Use GraphQL

  • ✓Multiple clients (web, mobile) consuming the same API with different data needs
  • ✓Rapidly evolving product where frontend needs frequently change
  • ✓Aggregating data from multiple microservices or databases
  • ✓Applications requiring real-time data with subscriptions
  • ✗Simple CRUD APIs with few clients — REST may be simpler and faster to build

GraphQL vs REST

AspectGraphQLREST
EndpointsSingle /graphqlMultiple per resource
Over/Under-fetchingEliminated by designCommon problem
VersioningSchema evolution (add fields, deprecate)/v1, /v2 URL versioning
CachingClient-side (normalized) or APQHTTP cache (CDN-friendly)
Learning CurveHigher (SDL, resolvers, N+1)Lower (HTTP + JSON)

GraphQL Tips & Best Practices

✅

Use DataLoader for every relationship

Any field that resolves a related type (e.g., Post.author) will cause N+1 queries without DataLoader.

✅

Always validate in mutations

Throw GraphQLError with code: BAD_USER_INPUT for validation errors so clients can display them properly.

✅

Name all operations

Named operations appear in Apollo Studio traces and error logs. Anonymous queries are much harder to debug in production.

✅

Co-locate fragments with components

Define the fragment a component needs next to that component (Apollo Client supports this). Prevents over-fetching at the component level.

⚠️

Limit query depth and complexity

Without depth limits, a malicious client can craft a deeply nested query that recursively loads millions of DB rows.

⚠️

Never expose raw DB errors

Use formatError to sanitize errors in production. Stack traces and DB error messages reveal sensitive implementation details.

💡

Use cursor pagination for large datasets

Offset pagination breaks when items are added or deleted between pages. Cursor-based pagination (Relay spec) stays consistent.

💡

Prefer the union error pattern

Returning union types from mutations (Success | Error) is type-safe and explicit compared to throwing errors that break partial responses.