# GraphQL

GraphQL is a query language and runtime for APIs that lets clients request exactly the fields they need from a typed schema in a single request.

## Overview

GraphQL is a query language for APIs and a runtime for answering those queries. A server publishes a typed schema describing the data it can provide and the operations it allows. A client sends a query that names exactly the fields it wants, possibly following relationships across several types, and gets back a JSON response with the same shape. One request can gather data that would otherwise need several calls to different REST endpoints.

An everyday analogy is ordering at a deli counter rather than buying pre-packed sandwiches. With pre-packed sandwiches you take what is in the box, even the tomato you do not want, and you may need three boxes to get everything for lunch. At the deli counter you say "rye bread, turkey, no tomato, add pickles", and you get exactly that in one go. A REST endpoint is the pre-packed sandwich; a GraphQL query is the order at the counter. The counter, though, needs staff and rules so that nobody orders something absurd that takes an hour to make.

GraphQL is common for front ends and mobile apps that show data from many back-end systems on one screen. It also appears in AI work: its typed schema and introspection make it a convenient interface for language-model tools and agents, and a schema can be exposed through the [Model Context Protocol](/tools/mcp) or used with [function calling](/tools/function-calling) so a model can request only the data it needs.

## What it is

GraphQL has a small set of building blocks.

| Concept | What it is |
|---|---|
| Schema | The typed contract of everything the API offers, written in the schema definition language (SDL) |
| Object type | A named type with fields, such as `Order` with `id`, `total` and `customer` |
| Scalar | A leaf value type: `Int`, `Float`, `String`, `Boolean`, `ID`, plus custom scalars |
| Query | A read operation, the entry point for fetching data |
| Mutation | A write operation that changes data and returns a result |
| Subscription | A long-lived operation where the server pushes results when events happen |
| Resolver | Server function that produces the value for a field |
| Introspection | The ability to query the schema itself, used by tools and editors |
| Fragment | A reusable selection of fields |
| Variables | Typed inputs passed separately from the query text |

GraphQL is transport-agnostic, but in practice it is usually served over HTTP at a single endpoint, with subscriptions often carried over WebSockets.

## Where it came from

Facebook created GraphQL in 2012 for its mobile apps, which needed to load complex, nested news feed data efficiently over slow mobile networks. Lee Byron, Nick Schrock and Dan Schafer are credited as its co-creators. Facebook published the specification and a JavaScript reference implementation in 2015.

In late 2018 the project moved to the newly formed GraphQL Foundation, hosted by the Linux Foundation, so that its governance became vendor-neutral. The specification is released in editions; after the October 2021 edition, the next was published in September 2025. Implementations exist for most server languages, and ecosystems such as Apollo, Relay and various federation approaches grew around it.

## How it works

### 1. Define the schema

The server team writes types and the root operations:

```graphql
type Customer { id: ID!  name: String!  orders(last: Int = 5): [Order!]! }
type Order    { id: ID!  total: Float!  status: String! }
type Query    { customer(id: ID!): Customer }
type Mutation { cancelOrder(id: ID!): Order }
```

The exclamation mark means non-null.

### 2. Write resolvers

Each field has a resolver. `Query.customer` might read from a database; `Customer.orders` might call an orders service. Resolvers receive the parent object, arguments and a context holding things like the authenticated user.

### 3. Client sends an operation

A client sends a query such as `{ customer(id: "c42") { name orders(last: 3) { total status } } }`, usually as a POST with variables in JSON.

### 4. Parse and validate

The server parses the document and validates it against the schema: every field must exist, arguments must have correct types, and fragments must apply. Invalid queries are rejected before any resolver runs.

### 5. Execute

The executor walks the selection, calling resolvers field by field. Queries can resolve sibling fields in parallel; mutations run their top-level fields in order. Results are assembled into JSON matching the request shape.

### 6. Return data and errors

The response contains a `data` object and, if something failed, an `errors` list with messages and paths. Partial data can be returned alongside errors.

### 7. Optimise

Servers use batching tools such as DataLoader to avoid the "N+1" problem, where resolving a list triggers one back-end call per item. They also cache, limit query depth and cost, and often use persisted queries so only known operations are accepted.

## Key concepts and terms

| Term | Meaning |
|---|---|
| SDL | Schema definition language for writing GraphQL types. |
| Operation | A query, mutation or subscription sent by a client. |
| Selection set | The fields requested inside braces. |
| Resolver | A function that produces a field's value. |
| Overfetching | Receiving more data than needed, common with fixed endpoints. |
| Underfetching | Needing extra requests to get all required data. |
| N+1 problem | One query causing many repeated back-end calls for list items. |
| DataLoader | A batching and caching utility that groups back-end lookups. |
| Persisted query | A pre-registered operation referenced by ID or hash. |
| Federation | Composing one graph from schemas owned by several services. |
| Introspection | Querying the schema's own structure. |
| Query cost analysis | Estimating and limiting the work a query would cause. |

## A worked example

This scenario is illustrative. A travel app's booking screen shows the traveller's name, their next three trips, for each trip the hotel name and the weather forecast. With REST, the app calls `/users/u7`, `/users/u7/trips?limit=3`, then `/hotels/{id}` three times and `/weather?city=` three times: eight round trips. On a mobile network with 150 ms latency, the screen takes over a second to fill, and the user endpoint returns 60 fields when 1 is needed.

The team adds a GraphQL layer in front of the existing services. The schema has `User`, `Trip`, `Hotel` and `Forecast` types. The app sends one query asking for `user { name trips(next: 3) { start hotel { name } forecast { highC summary } } }`. Resolvers call the same REST services internally, inside the data centre where latency is about 2 ms. DataLoader batches the three hotel lookups into one call.

The screen now needs one round trip, and the response is 1.1 KB instead of 14 KB. The team sets a maximum depth of 6, a cost limit, and switches the production app to persisted queries so arbitrary queries from outside are rejected. Monitoring per resolver reveals the weather service is the slowest part, at 80 ms, so they add a 10-minute cache for forecasts.

## Strengths and limitations

**Strengths:**

- Clients get exactly the data they need in one request.
- A strongly typed schema serves as documentation and contract.
- Introspection powers editors, code generation and validation.
- The schema can evolve by adding fields and deprecating old ones without versioned URLs.
- A single graph can unify many back-end services.

**Limitations:**

- HTTP caching is harder because most requests are POSTs to one endpoint.
- Flexible queries can be expensive; depth and cost limits are needed.
- The N+1 problem appears easily without batching.
- Authorisation must be enforced per field or type, not per endpoint.
- File uploads and very large binary payloads are awkward.
- More server complexity than simple REST for small APIs.

## How it compares with alternatives

| Option | Style | When it fits better |
|---|---|---|
| REST | Resource endpoints with HTTP methods and caching | Simple, cacheable, public APIs |
| [gRPC](/tools/grpc) | Typed RPC over HTTP/2 with binary messages | Internal service-to-service calls and streaming |
| OData | Query conventions over REST | Enterprise data APIs in ecosystems that use it |
| Backend-for-frontend REST | One tailored REST API per client app | Few clients with stable screens |
| Server-sent events or WebSockets | Push channels | Real-time updates where GraphQL subscriptions are unnecessary |

## Practical guidance and best practices

1. Design the schema around client needs and domain concepts, not around database tables.
2. Use DataLoader or similar batching for every list relationship.
3. Enforce depth limits, cost limits and timeouts on queries.
4. Use persisted queries for first-party apps in production.
5. Apply authorisation inside resolvers or a policy layer, never only at the gateway.
6. Evolve with additive changes and `@deprecated`; remove fields only after usage drops to zero.
7. Disable or restrict introspection on public production endpoints if the schema is sensitive.
8. Trace resolvers with [OpenTelemetry](/tools/opentelemetry) to find slow fields.
9. Paginate lists with cursors rather than returning unbounded arrays.

## Common mistakes

- Exposing a schema that mirrors database tables, leaking internal structure.
- Allowing unlimited nested queries, which attackers can use for denial of service.
- Forgetting field-level authorisation, so sensitive fields leak through a different path.
- Returning HTTP 200 for errors and not monitoring the `errors` array.
- Using GraphQL for a tiny API where three REST endpoints would do.
- Ignoring N+1 calls until load testing reveals thousands of back-end requests.

## Frequently asked questions

### Does GraphQL replace REST?

Not necessarily. Many organisations use GraphQL for client-facing aggregation and keep REST or gRPC between services. The choice depends on clients, caching needs and team skills.

### Is GraphQL a database?

No. It is an API layer. Resolvers fetch data from databases, services or files; GraphQL describes and assembles the response.

### How are errors reported?

Responses can include an `errors` array with messages and paths alongside partial `data`. HTTP status codes are often 200 even when some fields fail, so monitoring must inspect the body.

### Is GraphQL secure?

It can be, with the same care as any API: authentication, field-level authorisation, input validation, query cost limits and rate limits. See [OWASP](/tools/owasp) guidance on API security.

### What is federation?

Federation composes a single graph from subgraphs owned by different teams or services, with a gateway or router planning queries across them.

### Who maintains the specification?

The GraphQL Foundation under the Linux Foundation maintains the specification, which is published in editions, most recently in September 2025.

## Glossary

- Schema: the typed description of a GraphQL API.
- Field: a named piece of data on a type.
- Resolver: the function behind a field.
- Mutation: an operation that changes data.
- Subscription: a push-based operation for events.
- Fragment: a reusable set of fields.
- Directive: an annotation such as `@deprecated` or `@include`.
- Introspection: querying the schema itself.
- Cursor pagination: paging through lists with opaque position markers.

## Summary

GraphQL lets clients ask a typed schema for exactly the fields they need in one request, with resolvers fetching data from any back end. Created at Facebook in 2012, released in 2015 and now governed by the GraphQL Foundation, it suits apps that combine data from many sources and benefit from a strong contract. It shifts complexity to the server: batching, query cost limits, field-level authorisation and careful caching are essential. Use it where client flexibility pays off, and keep simpler REST or gRPC where it does not.
