GraphQL

A query language for APIs and a server-side runtime for executing those queries — developed by Facebook (2012, open-sourced 2015) — where clients specify exactly what data they need, and the server returns precisely that.

Problem

REST APIs return fixed response shapes. Clients (especially mobile) often need only a subset of a resource’s fields (over-fetching) or must make multiple requests to assemble a view (under-fetching). Versioning REST APIs as requirements evolve creates URI proliferation and maintenance burden.

Solution / Explanation

GraphQL replaces multiple REST endpoints with a single endpoint (/graphql) that accepts structured queries. Clients describe the shape of the data they want; the server resolves and returns exactly that shape — no more, no less.

Core Operations

Query — read data.

query {
  user(id: "1") {
    name
    email
    posts {
      title
    }
  }
}

Returns only name, email, and each post’s title.

Mutation — write data.

mutation {
  createPost(title: "Hello", body: "World") {
    id
    title
  }
}

Subscription — real-time push via WebSocket.

subscription {
  newComment(postId: "42") {
    author
    body
  }
}

Type System and Schema

GraphQL is strongly typed. The schema is the contract between client and server:

type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]!
}
 
type Post {
  id: ID!
  title: String!
  author: User!
}
 
type Query {
  user(id: ID!): User
  posts: [Post!]!
}
  • ! means non-nullable.
  • Scalar types: Int, Float, String, Boolean, ID.
  • Object types, interfaces, unions, enums, and input types are all supported.

Resolvers

Each field in the schema is backed by a resolver function — a function that fetches the data for that field. The resolver for User.posts knows how to query the database for posts belonging to a user.

The N+1 Problem

A classic GraphQL performance trap: fetching a list of N users, then for each user running a separate query to fetch their posts — resulting in N+1 database queries. The standard solution is the DataLoader pattern (batching and caching resolver calls within a request).

Unverified

DataLoader is the de-facto solution in JavaScript/TypeScript ecosystems. Equivalent batching libraries exist in other languages but vary in maturity.

Introspection

GraphQL schemas are introspectable at runtime. Tools like GraphiQL and Apollo Studio use introspection to generate documentation and enable query auto-completion.

Key Components

  • Schema — the strongly-typed contract defining all available types and operations.
  • Query / Mutation / Subscription — the three operation types.
  • Resolver — server-side function that fetches data for a field.
  • Type System — scalar, object, interface, union, enum, input types.
  • DataLoader — batching pattern that solves N+1 resolver queries.
  • Introspection — self-describing schema discoverable at runtime.

When to Use

  • Clients with diverse data needs (mobile app, web app, third-party integrations).
  • Rapid front-end development where clients own their data shape.
  • Aggregating data from multiple backend services into a single query (API gateway pattern).
  • Applications where bandwidth is constrained (mobile): avoid over-fetching.

Trade-offs

BenefitCost
Eliminates over-fetching and under-fetchingN+1 problem requires DataLoader discipline
Single endpoint simplifies routingHTTP caching is harder (POST requests)
Strongly typed schema is self-documentingSchema design requires up-front investment
Clients evolve independently of server schemaComplex queries can be expensive — rate limiting/query complexity needed
Introspection enables great toolingLearning curve for server-side resolver architecture

Comparison with REST and gRPC

RESTGraphQLgRPC
ProtocolHTTP/1.1HTTP/1.1HTTP/2
PayloadJSON/XMLJSONProtobuf
Data fetchingServer-defined shapeClient-defined shapeServer-defined shape
CachingNative HTTPRequires custom cacheNo native HTTP caching
Type safetyOptional (OpenAPI)Built-inBuilt-in (Protobuf)
StreamingWebhooks / SSESubscriptions (WebSocket)Native streaming
Best forResource CRUDFlexible data fetchingLow-latency internal RPC