Skip to main content

GraphQL

A query and schema definition language specification for APIs, providing a client-specified runtime to request exact data shapes.

Current versionSeptember 2025
Specificationspec.graphql.org
Updated2026-10-14

What is GraphQL?

GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. It was developed internally by Facebook in 2012 before being publicly released in 2015, and is now hosted by the GraphQL Foundation.

By providing a complete and understandable description of the data in your API, GraphQL gives clients the power to ask for exactly what they need and nothing more. This mitigates common REST issues like over-fetching (retrieving unnecessary fields) and under-fetching (needing to make multiple round-trips to nested endpoints).

The specification is published in dated editions at spec.graphql.org. The current edition is September 2025, which replaced the October 2021 edition; work in progress lives in the working draft.


1. Architecture & Resolver Engine

In a GraphQL architecture, client applications send query payloads detailing the exact fields they require. The GraphQL execution engine processes this request against a strongly typed Schema, resolving fields through dedicated execution handlers.

The GraphQL Execution Pipeline

When a client requests data from a GraphQL API, the engine processes the operation through a series of structured steps:

  1. Request: The client sends a single HTTP request to the /graphql endpoint containing the query string (selection set) and variable parameters, almost always as a POST with a JSON body.
  2. Parsing & Validation: The GraphQL Engine parses the query and validates it against the strongly typed Schema to check for syntax errors, unknown fields, or security depth violations.
  3. Resolver Invocation: The execution engine invokes dedicated Field Resolvers for each requested node in the query selection tree.
  4. Data Fetching: Resolvers run concurrently to fetch information from underlying systems, performing SQL database reads, hitting key-value caches like Redis, or routing requests to external REST microservices.
  5. Payload Serialization: The engine packages the resolved values into a single JSON object matching the exact shape of the client’s original query, returning it to the application.

2. Schema Definition Language (SDL)

At the heart of any GraphQL service is a schema, which defines the types, fields, and operations available. The schema is written using the standard Schema Definition Language (SDL).

Core Types

# Object Type representing a catalog resource
type User {
id: ID!
name: String!
email: String!
role: UserRole!
posts(limit: Int): [Post!]!
}
# Enum Type restricting role values
enum UserRole {
ADMIN
EDITOR
MEMBER
}
# Nested Object Type
type Post {
id: ID!
title: String!
content: String
author: User!
}

Operation Root Types

Every schema defines root operation types: Query (read), Mutation (write), and Subscription (real-time stream).

type Query {
me: User
userById(id: ID!): User
searchPosts(query: String!): [Post!]!
}
type Mutation {
createPost(title: String!, content: String): Post!
deletePost(id: ID!): Boolean!
}

3. Operation Types

Clients execute operations by sending payloads specifying the operation type, name, variables, and selection set.

1. Queries (Read)

Unlike REST endpoints where the payload structure is determined by the server, GraphQL queries let the client select specific properties.

# Request
query GetMyProfile {
me {
name
role
posts(limit: 2) {
title
}
}
}
// Response
{
"data": {
"me": {
"name": "Steve McDougall",
"role": "ADMIN",
"posts": [
{ "title": "Understanding HTTP Caching" },
{ "title": "Getting Started with OpenAPI 3.1" }
]
}
}
}

2. Mutations (Write)

Mutations are used to create, update, or delete data on the server. Like queries, mutations return resolved objects, allowing immediate client UI updates.

# Request
mutation CreateNewArticle($title: String!, $body: String!) {
createPost(title: $title, content: $body) {
id
title
author {
name
}
}
}

4. Comparison: OpenAPI (REST) vs. GraphQL

Feature OpenAPI / REST GraphQL
Data Fetching Multi-endpoint; server defines response shape. Single endpoint (/graphql); client defines shape.
Typing & Validation JSON Schema (external validation). Strongly typed Schema (built-in parser validation).
Versioning URI paths (/v1/), header versioning, or media type. Versionless; deprecated fields are marked (@deprecated).
Real-time Webhooks, WebSockets, or Server-Sent Events (SSE). Native Subscriptions via WebSockets or SSE.
Caching Native HTTP Caching (ETag, Cache-Control). Client-side/Application layer caching (Apollo/Relay).

Best Practices in GraphQL Design

  1. Follow GraphQL over HTTP: The GraphQL Foundation’s GraphQL over HTTP specification is still a working draft, but it is the closest thing to a standard for the transport. Servers must accept POST with a Content-Type: application/json body containing query, variables and operationName. Servers may also accept GET with those values in the query string, which makes queries cacheable by ordinary HTTP caches, but a mutation must never execute over GET: reject it with 405 Method Not Allowed and an Allow header.
  2. Use application/graphql-response+json and meaningful status codes: Clients should send Accept: application/graphql-response+json. With that media type, a response with no data entry is a request error (unparseable JSON or document, failed validation, and so on) and gets a 4xx or 5xx status, not 200 OK. A result that contains data is a success or a partial success and keeps a 2xx status, with field errors in the errors array alongside the partial data. The habit of answering everything with 200 OK comes from plain application/json responses, which the draft keeps only for legacy clients that do not accept the new media type.
  3. Prevent Resolver N+1 Inefficiency: Use batching utilities like DataLoader to collapse nested database queries into bulk reads, preventing execution query overload.
  4. Enforce Query Depth Limits: Restrict maximum query nested depth and complexity analysis to protect the GraphQL engine against denial-of-service recursion attacks.
On this page (6 sections)