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.