What is GraphQL?

GraphQL is a query language for APIs and a runtime for executing those queries. Originally developed internally at Facebook in 2012 to power their mobile apps, it was open-sourced in 2015 and has since been adopted by companies including GitHub, Twitter, Shopify, Airbnb, and The New York Times.

Unlike traditional REST APIs — which expose a fixed set of endpoints each returning a predetermined data structure — GraphQL exposes a single endpoint where clients describe precisely what data they need. The server returns exactly that, no more and no less.

Think of REST as a restaurant with a fixed menu: you order a meal and get what comes with it. GraphQL is more like ordering off the menu item by item: you specify each ingredient you want, and the kitchen assembles exactly that plate.

GraphQL is not a library, framework, or database — it is a specification. Implementations exist in virtually every language, including JavaScript (Apollo Server, graphql-js), Python (Strawberry, Graphene), Ruby (graphql-ruby), Go (gqlgen), Java (graphql-java), and many more.

How GraphQL Works

A GraphQL API operates through three core concepts: a schema, resolvers, and operations.

The schema is a type system written in the GraphQL Schema Definition Language (SDL). It describes every data type your API exposes and the operations clients can perform. The schema acts as a contract between the client and server.

Resolvers are functions that fulfil each field in the schema. When a client requests a field, the resolver for that field runs and fetches the data — from a database, a REST API, a cache, or anywhere else.

Operations are what clients send to the API. There are three types: queries (read), mutations (write), and subscriptions (real-time updates). All operations are sent as POST requests (or GET for queries) to a single endpoint, typically /graphql.

Here is a simple example. Suppose you have a blogging platform. With REST, to show a post and its author's name, you might need two requests: GET /posts/42 and GET /users/7. With GraphQL, you send one query:

query {
  post(id: "42") {
    title
    body
    author {
      name
    }
  }
}

The server returns exactly that structure — no extra fields, no extra round trips.

REST vs GraphQL: Key Differences

FeatureRESTGraphQL
EndpointsMultiple (one per resource)Single endpoint
Data fetchingFixed response structureClient specifies exact fields
Over-fetchingCommon — extra fields returnedNever — only requested fields returned
Under-fetchingCommon — requires multiple requestsEliminated — one query fetches all
VersioningURL versioning (/v1/, /v2/)Schema evolution, deprecation
HTTP CachingBuilt-in (GET requests cacheable)Requires custom caching (e.g. persisted queries)
Type systemOptional (OpenAPI/Swagger)Built-in and required
Learning curveLowModerate
ToolingMature, widespreadRich but more specialized
Best forSimple, public APIsComplex, multi-client apps

Queries, Mutations, and Subscriptions

GraphQL has three operation types, each serving a distinct purpose.

Queries

Queries fetch data. They are analogous to GET requests in REST. Queries are declarative — you write what you want in a structure that mirrors the response shape:

query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
    posts {
      title
      createdAt
    }
  }
}

Mutations

Mutations modify data — creating, updating, or deleting records. They are analogous to POST, PUT, PATCH, and DELETE in REST:

mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    createdAt
  }
}

Subscriptions

Subscriptions enable real-time functionality. They maintain a persistent connection (typically via WebSocket) and push updates to the client when data changes. This is useful for live feeds, chat applications, and dashboards:

subscription OnCommentAdded($postId: ID!) {
  commentAdded(postId: $postId) {
    content
    author { name }
  }
}

Understanding the GraphQL Schema

The schema is the backbone of every GraphQL API. Written in SDL (Schema Definition Language), it defines types and the relationships between them. Here is a minimal example:

type Post {
  id: ID!
  title: String!
  body: String!
  author: User!
  comments: [Comment!]!
}

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

type Query {
  post(id: ID!): Post
  posts: [Post!]!
  user(id: ID!): User
}

type Mutation {
  createPost(title: String!, body: String!): Post!
}

The exclamation mark (!) means a field is non-nullable. The Query type defines all read operations. The Mutation type defines all write operations. Every field in the schema must have a corresponding resolver function on the server.

Key tip: GraphQL's introspection feature lets clients query the schema itself using __schema and __type. This powers tools like GraphiQL and Apollo Studio, which provide auto-complete and documentation from your live schema automatically.

Advantages of GraphQL

No Over-fetching or Under-fetching

With REST, an endpoint for a user profile might return 40 fields when your mobile app only needs 3. GraphQL eliminates this by letting the client specify exactly what it needs. This is especially valuable on mobile networks where bandwidth matters.

Single Request for Complex Data

Fetching a blog post with its author, comments, and each comment's author in REST might require 4+ API calls. In GraphQL, one query retrieves all of it. Fewer round trips means faster pages.

Strongly Typed Schema

The schema enforces types at the API boundary. Clients always know what fields are available and what types they return. This makes code generation, IDE auto-complete, and automated testing straightforward.

Evolve Without Breaking Changes

REST APIs often require versioning when the shape of a response changes. GraphQL handles evolution through deprecation: you can mark fields as deprecated and add new fields without creating a new API version, giving clients time to migrate.

Excellent Developer Experience

Tools like GraphiQL, Apollo Studio, and Insomnia provide interactive playgrounds where you can explore your schema, write queries with auto-complete, and inspect results — all in the browser.

Disadvantages and Trade-offs

HTTP Caching Is Harder

REST GET requests are naturally cacheable by browsers and CDNs. GraphQL sends queries as POST requests by default, which are not cached. Solutions exist (persisted queries, GET requests for read-only queries, Apollo Client's in-memory cache), but they add complexity.

N+1 Query Problem

If your resolvers are naive, fetching a list of 100 posts and their authors might trigger 100 separate database queries — one per author. This "N+1 problem" requires deliberate solutions, most commonly the DataLoader batching pattern.

File Uploads Are Awkward

GraphQL was not designed with file uploads in mind. The multipart request specification for GraphQL exists but is non-standard, and many implementations handle it differently.

Overkill for Simple APIs

If your API has three or four straightforward endpoints, GraphQL adds significant overhead: schema definition, resolver wiring, and tooling setup. REST is much simpler for simple use cases.

When to Use GraphQL vs REST

Choose GraphQL when: you have multiple clients (web, mobile, desktop) with different data needs; your data model is complex with many relationships; you want to avoid versioning headaches; or you're building a product where developer experience and rapid iteration matter.

Choose REST when: you're building a simple or public API; you need straightforward HTTP caching; your team is unfamiliar with GraphQL; you're building file-heavy APIs; or you need maximum compatibility with third-party tooling.

Use both when: many mature companies run both. REST for public-facing, cacheable, simple endpoints. GraphQL for internal, client-driven, complex data operations. There is no rule that says you must pick one.

Real-world note: GitHub's API v4 is GraphQL. Shopify's Storefront API is GraphQL. Twitter/X uses both. Facebook uses GraphQL internally at massive scale. These are strong signals that GraphQL handles production demands well — but each of these companies still maintains REST APIs too.