GraphQL#

GraphQL is a query language for HTTP APIs and the matching server runtime. A single endpoint serves a typed schema; the client asks for exactly the fields it wants and gets back exactly that. Two languages live inside one technology: the Schema Definition Language (SDL) on the server side, and the query language on the client side.

GraphQL fits between REST and gRPC. REST is resource-oriented and untyped at the wire; gRPC is RPC-shaped and binary; GraphQL is type-driven, schema-first, JSON over HTTP. Common in product APIs (GitHub, Shopify, the public Anthropic console API), less common in internal service-to-service traffic.

SDL#

The schema is the server’s contract. Types, fields, the relationships between them, the operations clients can run.

type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  body: String
  author: User!
}

type Query {
  user(id: ID!): User
  posts(limit: Int = 10): [Post!]!
}

type Mutation {
  createPost(title: String!, body: String): Post!
  deletePost(id: ID!): Boolean!
}

type Subscription {
  postAdded: Post!
}

Type markers.

Marker

Meaning

String

scalar type (also Int, Float, Boolean, ID). ID is a string by convention, opaque to the client.

!

non-null. String! means the field never returns null.

[T]

list of T. [T!]! means non-null list of non-null items.

type X { ... }

object type.

input X { ... }

input object (only for arguments).

enum X { A B C }

enumeration.

interface X, union Y

polymorphism (object types that share fields, or value that can be one of several types).

The three root types Query, Mutation, and Subscription declare what clients can send.

Query#

A client picks fields out of the schema. The structure of the response mirrors the structure of the query.

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

Response.

{
  "data": {
    "user": {
      "name": "rk",
      "email": "rk@example.com",
      "posts": [
        {"title": "Hello"},
        {"title": "Operator's notes"}
      ]
    }
  }
}

Variables, aliases, fragments.

query GetUser($id: ID!) {
  primary: user(id: $id) {           # alias
    ...UserSummary                   # fragment spread
  }
}

fragment UserSummary on User {
  id
  name
  email
}

# variables sent alongside the request body
# { "id": "42" }

Mutation#

A write operation. Same structure as a query; the convention is to ask for the new state in the response so the client can update its cache.

mutation {
  createPost(title: "Hello", body: "world") {
    id
    title
    author { name }
  }
}

Subscription#

A server-pushed stream. Typically over WebSocket (graphql-ws protocol). The client subscribes; the server emits events that match the subscription’s structure.

subscription {
  postAdded {
    id
    title
  }
}

Directives#

Annotations on queries and schemas. Two built-in.

query GetUser($skipEmail: Boolean!) {
  user(id: "42") {
    name
    email @skip(if: $skipEmail)
    posts @include(if: false)        # always omitted
  }
}

Schema directives (@deprecated, @auth, @cacheControl) attach metadata that the server or gateway reads.

N+1 problem#

The cost of GraphQL’s “fetch what you want” model is that a single query can fan out to many database round-trips. DataLoader (and its ports across languages) batches and caches per-request to dedup these.

// Node example
const userLoader = new DataLoader(async (ids) => {
  const users = await db.users.findMany({ where: { id: { in: ids } } });
  return ids.map(id => users.find(u => u.id === id));
});

Every GraphQL server framework ships some flavour of this.

Federation#

Apollo Federation and the open-source GraphQL Federation spec compose multiple subgraphs (one per microservice) into one supergraph behind a single endpoint. Each subgraph owns its types; the gateway plans the cross-service query.

Server libs#

Stack

Library

JS / TS

graphql-js, Apollo Server, Yoga, Pothos.

Python

Strawberry (typed), Graphene, Ariadne (schema-first).

Go

gqlgen (schema-first, codegen), graphql-go.

Rust

async-graphql, Juniper.

Java / Kotlin

graphql-java, DGS (Netflix).

Client libs#

Stack

Library

JS / TS

Apollo Client, urql, Relay, graphql-request (minimal).

Python

gql.

Go

genqlient (typed), graphql-go/client.

Generic

any HTTP client (POST JSON to /graphql).

For typed clients, the workflow is schema + queries → generated code: write .graphql files alongside the source, generate types and resolver bindings, import them.

Tooling#

GraphQL vs. REST vs. gRPC#

REST

GraphQL

gRPC

Style

resource

schema-driven query

typed RPC

Wire

JSON over HTTP

JSON over HTTP

protobuf over HTTP/2

Endpoint structure

many URLs

one URL, query selects

one URL per method

Versioning

URL or header

additive schema evolution

protobuf tags + reserved

Browser-friendly

native

native

via gRPC-Web / Connect

Reach for it

simple CRUD APIs, public

product APIs with rich client-driven structures, many client variations

internal service-to-service traffic, polyglot teams

References#