(Alert!)5 Things Even AI Can't Do, GraphQL GraphQL, created at Facebook in 2012 and now governed by the GraphQL Foundation, is a query language and runtime that allows clients to request exactly the data they need from a single endpoint, solving over-fetching and under-fetching issues common in REST APIs. It is transport- and storage-agnostic, enabling resolvers to pull data from various sources like PostgreSQL, REST microservices, or gRPC. The guide covers schema definitions, resolvers, the N+1 problem, federation, and security for production use. NEWS: MY GAME JUST LAUNCHED If you have built more than a couple of APIs, you have probably felt the friction of REST at scale. You ship an endpoint, the frontend team asks for one more field, you version the route, the mobile team needs a different shape of the same data, and six months later you are maintaining /v3/users/:id/full next to /v2/users/:id/summary and nobody remembers which one the Android app actually calls. GraphQL was built to kill that exact pain. It is a query language and runtime that lets clients ask for precisely the data they need — no more, no less — from a single endpoint, against a strongly typed schema that doubles as living documentation. This guide walks through GraphQL from first principles to production concerns. It is aimed at working developers, so expect schema definitions, resolvers, real queries, the N+1 problem, federation, security, and the parts of the ecosystem that actually matter in 2026. By the end you should be able to decide whether GraphQL belongs in your stack and how to build it without shooting yourself in the foot. GraphQL is a specification, not a library or a framework. It was created at Facebook in 2012 to power their mobile apps, open-sourced in 2015, and is now governed by the GraphQL Foundation under the Linux Foundation. The spec defines a query language, a type system, and an execution model — but it deliberately says nothing about which database you use, which programming language you implement it in, or how you transport requests over the wire. That last point trips people up, so let it sink in: GraphQL is transport-agnostic and storage-agnostic. Most implementations run over HTTP with JSON, but that is a convention, not a requirement. Your resolvers can pull data from PostgreSQL, a REST microservice, a gRPC backend, Redis, a flat file, or three of those at once. GraphQL sits as a thin coordination layer in front of whatever you already have. The mental model is simple. You describe your data as a graph of types . Clients write queries that traverse that graph. The server resolves each requested field by running a function. The response mirrors the shape of the query exactly. Here is the canonical hello-world. A query: query { user id: "4" { name email posts last: 3 { title } } } And the response: { "data": { "user": { "name": "Mary Watson", "email": "mary@example.com", "posts": { "title": "On the Nature of APIs" }, { "title": "Why I Stopped Versioning" }, { "title": "Cursors, Not Offsets" } } } } Notice three things. The response shape matches the query shape one-to-one. You got exactly the fields you asked for and nothing else. And you fetched a user and their posts in a single round trip, even though those are almost certainly two different tables or services on the backend. To appreciate GraphQL you have to be honest about where REST hurts. REST is excellent, widely understood, cache-friendly, and probably the right default for most public APIs. But it has three structural weaknesses that GraphQL attacks directly. Over-fetching. A REST endpoint returns a fixed payload. GET /users/4 might return forty fields when your screen needs two. On a desktop with fiber this is invisible. On a phone with spotty 4G, those extra kilobytes per request, multiplied across a list view, add up to real latency and battery drain. GraphQL clients request only the fields they render. Under-fetching and the N+1 round trip. The mirror image. To render a user profile with their recent posts and the comment count on each post, REST often forces you into a waterfall: fetch the user, then fetch their posts, then loop over posts fetching comments. Each step waits for the previous one. The frontend becomes a choreography of chained fetch calls. GraphQL collapses that into one request because the client describes the entire tree it wants up front. Endpoint proliferation and versioning. REST tends to grow an endpoint per view. A new screen needs a slightly different shape, so you add a route, or a ?include= parameter, or a ?fields= filter, and slowly reinvent a query language badly. GraphQL gives clients that flexibility natively. Because clients select fields explicitly, you can add new fields to a type without ever breaking existing clients — they simply do not ask for the new field. Most GraphQL APIs never version at all ; they evolve additively and deprecate fields with metadata rather than spinning up /v2 . The flip side, which we will cover, is that GraphQL trades these wins for harder caching, a more involved server setup, and new categories of performance and security concern. There is no free lunch. Everything in GraphQL starts with the schema . The schema is a contract written in the Schema Definition Language SDL that declares every type, every field, and every operation your API supports. It is strongly typed and introspectable, which is what powers autocomplete in tooling, codegen, and validation before a single resolver runs. Let's build out a small blog API to make the type system concrete. type User { id: ID name: String email: String role: Role posts: Post } type Post { id: ID title: String body: String published: Boolean author: User comments: Comment } type Comment { id: ID text: String author: User } enum Role { ADMIN EDITOR READER } A few things to unpack here, because the syntax is dense with meaning. Scalars are the leaf values. GraphQL ships with five built-in scalars: Int , Float , String , Boolean , and ID . ID is a string under the hood but signals "this is a unique identifier" to tooling. You can and should define custom scalars like DateTime , Email , or URL to add validation and semantic meaning. The exclamation mark means non-null. String is a string that will never be null. Post is a non-null list of non-null posts — the list itself is always present possibly empty , and no element inside it is ever null. Post would be a nullable list that may contain nulls. This nullability system is one of GraphQL's quietest strengths: it pushes a huge class of "cannot read property of undefined" bugs into the type checker. Object types like User and Post are the nodes of your graph, and the fields that point to other object types are the edges. User.posts connects a user to its posts; Post.author connects back. That bidirectional linking is exactly why it is called a graph . Beyond objects and scalars, the type system has a few more tools you will reach for constantly. Enums restrict a field to a fixed set of values, as Role shows above. Input types describe the structured arguments you pass into mutations — they look like object types but use the input keyword and cannot have fields that resolve to object types. Interfaces define a set of fields that multiple types must implement. Unions say a field can return one of several types that need not share any fields. Here is an interface and a union in practice: interface Node { id: ID } type Image implements Node { id: ID url: String altText: String } type Video implements Node { id: ID url: String durationSeconds: Int } union SearchResult = User | Post | Image input CreatePostInput { title: String body: String published: Boolean = false } The Node interface is the foundation of the Relay specification for global object identification — any type implementing Node can be refetched by its global id . Unions are perfect for search results or activity feeds where heterogeneous types share a list. Note the default value published: Boolean = false in the input — defaults are first-class in SDL. Finally, the three special root operation types are the entry points into the entire graph: type Query { user id: ID : User posts published: Boolean : Post search term: String : SearchResult } type Mutation { createPost input: CreatePostInput : Post deletePost id: ID : Boolean } type Subscription { postPublished: Post } Query is for reads, Mutation is for writes, and Subscription is for real-time streams. Every operation a client can perform must be reachable from one of these three roots. This is the complete public surface of your API in one readable document — which is precisely why a good schema is the single most important artifact in a GraphQL project. A query selects fields starting from the Query root and walking down the graph. The shape you write is the shape you get back. query GetDashboard { posts published: true { id title author { name } comments { text } } } Arguments like published: true can appear on any field, not just the root. You could ask for comments last: 5 deep inside the tree, and each field's resolver receives its own arguments. This is far more powerful than REST query parameters, which only apply to the endpoint as a whole. For anything beyond a hardcoded example you want variables rather than string interpolation. Variables keep your query static which matters for caching and persisted queries and let the client pass values separately: query GetUser $id: ID , $postCount: Int = 10 { user id: $id { name posts last: $postCount { title } } } { "id": "4", "postCount": 3 } Variables are declared in the operation signature with their types and optional defaults, then referenced with $ . Never build queries with string concatenation — it is the GraphQL equivalent of SQL injection waiting to happen, and it defeats caching. Aliases let you request the same field twice with different arguments, which would otherwise collide in the response object: query { recent: posts last: 5 { title } popular: posts orderBy: VIEWS, last: 5 { title } } Fragments are reusable selection sets. They keep queries DRY and, more importantly, are the mechanism that powers component-colocated data requirements in modern clients: fragment PostCard on Post { id title author { name } } query { posts published: true { ...PostCard } } At GraphQLConf 2025, fragments were a recurring theme, with the community converging on the idea that fragments are primarily for describing a UI component's data dependencies, not just for reuse . A