GraphQL

Tool

A way of designing a data endpoint where the caller writes down exactly which fields it needs and the server returns exactly that much.

Why it exists

For a long time, the way a server handed out data was one address, one response. There's an address that returns a list of documents and a separate one that returns user details. Whoever builds the server decides what goes into each bundle, and the caller takes the bundle whole.

That holds up fine while screens stay simple. The mismatch starts when a single screen mixes data from several places. Drawing one document list means calling for the list, then for each author's name, then for the comment counts — four or five round trips. On a phone with a slow connection, each of those trips turns directly into waiting. Waste runs in the other direction too: the list needs one name, and the user endpoint sends the signup date, the notification settings, and twenty other fields along with it.

At Pagedeck the problem showed up as a scheduling problem. When Noa proposed adding author avatars and recent comment counts to the document list, Theo said the screen code alone couldn't do it. The existing endpoint didn't return those two fields, so the server had to change and ship first, which meant the design work waited on a backend release. Every small change to a screen pulled backend scheduling into it, and the delay was rarely the work itself — it was waiting for a release window.

This approach reverses the order and removes that dependency. Instead of the server deciding what it will send, it publishes a list of what it can send, and the caller states what it wants on each request.

What it solves

It addresses the gap between the data a screen needs and the data a server hands over. The gap runs in two directions, and one mechanism closes both.

  1. Receiving too much. A place that needs one name gets twenty fields. Here the request names the one field and that's what comes back.
  2. Receiving too little and going back repeatedly. Instead of separate calls for the list, the authors, and the comment counts, all three are nested in one request and arrive together.

This works because the server publishes a schema — a written list of every field the endpoint can produce and the type of each one. The schema records relationships too (a document has a title and an author; an author has a name and an avatar), so the caller reads it and selects the branches it needs. Nothing outside that list can be requested, which is what keeps an open-ended request from becoming an open-ended door.

Having a single address is part of the design. Rather than opening a new address per feature, there's one endpoint and the request body says what's wanted. A new screen that needs a new combination of existing fields requires no server change at all. That's where the scheduling problem from earlier disappears.

The calling code gets simpler as well. The response comes back shaped like the request, so its structure is known in advance, and because field names and types are pinned down in the schema, a typo or a wrong type is caught before the request reaches the server. The schema doubles as documentation without a separate OpenAPI file.

Alternatives

Most teams start with REST. Fixed responses per address are easy to reason about, and because the same address returns the same result, caching is straightforward. Storing a response on an intermediate server and reusing it gets considerably harder when every request body differs.

For traffic between services inside one company, gRPC is common. It trades a human-readable format for a machine-readable one to gain speed, which suits internal communication better than a publicly exposed endpoint.

Problems that run in a different direction need different tools. To know the instant a payment clears, a webhook fits. For two people editing the same document at once, a WebSocket that holds the connection open fits.

Three questions decide the choice: whether several different kinds of clients call the same server, whether screen requirements change often, and whether the team running the endpoint has capacity for the extra work. When the first two are large and the third is covered, this approach pays off. When all three are small, fixed responses per address are the better deal. Letting anyone write a request also means anyone can write an expensive one, so depth limits, complexity limits, and a rate limit come with the territory. None of those are optional once the endpoint is open to callers outside the team.

How it sounds at work

"That field is already in the schema, so pull it on the client — no server release needed."

The data is already exposed, so the work doesn't have to wait on the backend.

"This query nests too deep. One request pulling that much will hurt the server."

A single request is dragging in too much data and should be split.

"Let's keep the public endpoint on REST. Caching matters more there."

With many different integrators and repeated identical responses, the older style wins.

At Pagedeck the web app and the mobile app call the same server. The two clients need different fields, so the team used to either maintain separate endpoints or let one client live with a response shaped for the other. Since the schema went up, Theo adds fields and Noa and the mobile developers each select the combination they need. Theo's job grew in exchange: every new field needs a check on what it costs to produce and whether it triggers repeated lookups inside a list. Adding a field is a few lines of code and an hour of thinking about what happens when a thousand people request it at once.

What people get wrong

The name leads many people to file it as a database technology, since writing a query looks a lot like talking to a database. It's a convention for the interface between programs and has nothing to say about what storage sits behind it. Confusing the two sends the adoption conversation sideways — either the team treats it as a risky storage migration, or it expects the change to fix storage performance problems it can't touch.

The other assumption is that it's always faster. Fewer round trips and smaller payloads make that a reasonable guess. But one request nesting several fields can increase the number of lookups inside the server. Fetch the author for each of 20 list items and the server makes 21 trips. Adopted without preparation, the screen makes a single call while the server runs slower than before — and since nothing in the client code shows the cause, the problem takes a long time to find. Batched loading on the server side is what turns the expected gain into a real one.

Easily confused with

TermWho decides the responseHow it differs
RESTThe server exposing the endpointEach address returns a fixed shape. Easier to learn and to cache, at the cost of changing the server whenever a screen changes

Where to go next

  • API

    see the full set of options this one sits among

  • REST

    read up on the most widely used alternative

Last updated 2026-09-16