What is GraphQL?

Last updated: July 2026

GraphQL is a query language for APIs and a server-side runtime that returns exactly the fields each client asks for in one request. The duality matters: the language is a specification any client can speak; the runtime executes those queries against a type system you define over your existing data — any database, any service. It is not a database, and it replaces neither your storage nor, necessarily, your REST API.

Key takeaways

QuestionAnswer
What it isA spec-governed query language + execution runtime — storage-agnostic
The signature moveResponse shape mirrors query shape: ask for fields, get those fields
The three operationsquery (read) · mutation (write) · subscription (real-time push)
The building blocksSchema (SDL contract) · types · resolvers (per-field fetch functions)
The honest billCaching strategy, N+1 batching, cost-based limits, security hardening

The signature demo: query and response

The demo every explanation converges on, because it is the idea — the response is the query, filled in:

# Request                              # Response
{                                      {
  post(id: "8fk2") {                     "data": {
    title                                  "post": {
    author {                                 "title": "Hello GraphQL",
      username                               "author": {
    }                                          "username": "ada"
    comments(first: 2) {                     },
      text                                   "comments": [
    }                                          { "text": "Nice." },
  }                                            { "text": "Ship it." }
}                                            ]
                                           }
                                         }
                                       }

One request, three related resources, zero unrequested fields — the overfetching and underfetching pair retired in a stroke. Calling it from real clients is plain HTTP:

// JavaScript / Node.js — query Back4app's auto-generated GraphQL API
const res = await fetch('https://parseapi.back4app.com/graphql', {
  method: 'POST',
  headers: {
    'X-Parse-Application-Id': APP_ID,
    'X-Parse-Client-Key': CLIENT_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: '{ posts(first: 20) { edges { node { title author { username } } } } }',
  }),
});
const { data } = await res.json(); // shaped exactly like the query

Schema, query, resolver: the working trio

Explainers show the query; almost none show the machinery behind it as one coherent picture. The schema is the typed contract, written in SDL:

type Post {
  title: String!          # ! = non-null
  author: User!
  comments(first: Int): [Comment!]
}

type Query {              # the read entry points
  post(id: ID!): Post
}

type Mutation {           # the write entry points
  createPost(title: String!): Post!
}

Resolvers are the runtime’s other half — one function per field, each free to fetch from anywhere:

const resolvers = {
  Query: {
    post: (_, { id }) => db.posts.findById(id),
  },
  Post: {
    author: (post) => db.users.findById(post.authorId),   // called per post!
  },
};

Execution is a pipeline: parse the query, validate it against the schema (invalid operations die before touching data), then walk the selection set calling resolvers and assemble the mirrored JSON. That per-field resolver call is also the flexibility’s price tag — note the // called per post!, which becomes the N+1 problem below. The name, which nobody explains: your data forms a graph of typed objects, and queries traverse it from root fields — though only along schema-exposed paths, not arbitrary traversals like a true graph query language.

Provenance, briefly: created at Facebook (now Meta) in 2012 for its mobile apps, open-sourced in 2015, governed since 2018 by the GraphQL Foundation under the Linux Foundation, current spec edition October 2021, with a GraphQL-over-HTTP draft standardizing the transport conventions.

Queries, mutations, subscriptions

GraphQL execution pipelineA client operation — query, mutation, or subscription — arrives at a single endpoint, is parsed and validated against the schema, executed by calling a resolver per requested field against databases or services, and returned as JSON mirroring the request shape.

Client operation
query · mutation · subscription

Single endpoint
/graphql

Parse + validate
against schema

Execute:
resolver per field

Databases,
APIs, services

JSON mirroring
the query shape

A client operation — query, mutation, or subscription — arrives at a single endpoint, is parsed and validated against the schema, executed by calling a resolver per requested field against databases or services, and returned as JSON mirroring the request shape.

Queries read. Mutations write — and select fields on the result, so the client gets the post-write state in the same round trip. Subscriptions keep a connection open (in practice WebSockets) and push events as they occur; they are GraphQL’s real-time story, with the caveat that each active subscription is server-held state. All three share the schema, the type system, and the tooling — one contract, three tenses.

GraphQL vs. REST

GraphQLREST
EndpointsOne (/graphql)One per resource
Response shapeClient-composed per queryFixed per endpoint
Over/underfetchingSolved at the HTTP layerMitigated by params
HTTP cachingLost by default (single POST)Native — the superpower
Typing & introspectionBuilt into the contractOpt-in via OpenAPI
VersioningVersionless evolution + @deprecated/v1, /v2 conventions
Real-timeSubscriptions in-specOut of scope
Best atDiverse clients, nested dataResource CRUD, cacheable reads

The full argument — including when REST is simply the better choice — lives in the dedicated GraphQL vs. REST entry.

Running GraphQL in production: the honest costs

The section vendor explainers soften. Caching: one POST endpoint forfeits URL-keyed HTTP and CDN caching; the replacement is client-side normalized caches keyed on id plus __typename, and persisted queries (safe-listed, hashed operations sent as GETs) to win some transport caching back. N+1: naive resolvers turn a 20-post list into 1 + 20 database reads — the same problem REST clients have over HTTP, relocated to your resolver layer, and fixed there by batching loaders like DataLoader. Errors: GraphQL returns 200 OK with an errors array — monitoring keyed on status codes goes blind unless taught otherwise. Rate limiting: requests are not equal when one query can nest ten relations; mature APIs meter query cost (depth and complexity analysis), not request count. Security: disable introspection in production, enforce depth and complexity limits, and keep authorization in the business layer under the resolvers — the single endpoint also blinds URL-based WAF rules, so validation moves into the GraphQL layer itself.

Common use cases

  • Mobile apps on constrained networks — the founding use case: exact fields, minimal bytes, fewer round trips.
  • Multi-client products — watch app, phone app, web dashboard, each shaping its own responses against one schema.
  • Backend-for-frontend aggregation — one GraphQL layer composing several internal services for consumption by UIs.
  • Rapidly evolving frontends — new screens select new fields without waiting for new endpoints.
  • Typed contracts end to end — schema introspection generating typed clients, keeping API and UI honest at compile time.

Should you use GraphQL? A decision matrix

GraphQL earns its machinery when…Prefer REST when…
Clients differ in the data they needOne client type, stable screens
Screens read nested, relational dataResources map cleanly to endpoints
Aggregating multiple backend sourcesOne service owns the data
Bandwidth is precious (mobile-first)HTTP/CDN caching can carry read load
A platform generates the schema for youThe team must hand-build and harden it all

Limitations and trade-offs

  • The flexibility is server-paid. Arbitrary client queries mean the server must be safe under any shape — batching, cost limits, and depth guards are prerequisites, not polish.
  • Caching becomes your project. What HTTP gave REST for free, GraphQL teams reimplement in client caches and persisted queries.
  • Observability needs relearning. One endpoint, always-200 responses, and per-field timing require GraphQL-aware tooling.
  • File uploads and binary data are awkward — commonly delegated to separate upload endpoints beside the graph.
  • Schema governance is organizational. A shared contract across teams needs ownership rules; federation (composing team-owned subgraphs into one supergraph) is the scaling answer, and its own discipline.

GraphQL on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. The distinctive part is where the schema comes from: define a data model and the platform generates the GraphQL API — typed object types, query and mutation fields, relation-traversing connections like the posts → author example above — with no resolvers to write, since Back4app implements them against your database with permissions enforced per request. The code tabs show the whole client story: one POST to /graphql with your app’s keys. A built-in GraphQL console covers exploration, REST remains available on the same data for cache-friendly reads, and custom logic joins the schema as Cloud Code functions — the honest-costs section above becomes largely the platform’s bill, not yours.

Frequently asked questions

What is GraphQL in simple terms?

A query language that lets a client ask an API for exactly the fields it needs — nested relations included — in one request, plus a server runtime that fulfills those requests from your existing data sources. It sits in front of any database or service; it is not a database itself.

Is GraphQL better than REST?

Neither is universally better. GraphQL wins with diverse clients, bandwidth constraints, and data aggregated from several sources; REST wins on HTTP caching, simplicity, and tooling maturity for resource-shaped CRUD. The dominant production pattern is pragmatic: a GraphQL layer for frontends over REST or RPC internals.

Is GraphQL a database or like SQL?

No — it is an application-layer API language, storage-agnostic by design: resolvers can read from any database, another API, or a file. And despite the name, it is not a general graph query language like SPARQL; you traverse the graph only along the paths the schema exposes.

What are queries, mutations, and subscriptions?

The three operation types. Queries read data; mutations write it — and return the new state in the same round trip, so the client updates without a follow-up fetch; subscriptions push real-time updates over a persistent connection, typically WebSockets. All three are validated against the same schema.

What is a GraphQL schema?

The typed contract between client and server, written in the Schema Definition Language: object types, their fields, and the root Query, Mutation, and Subscription entry points. Every incoming operation is validated against it before execution, and tooling introspects it to generate docs and typed clients.

What is a resolver?

A server-side function that fetches the value of one field — from a database, another API, or anywhere. The runtime walks each query and calls the resolver for every requested field, which is both the flexibility of GraphQL and the origin of its N+1 problem when list-item resolvers each query separately.

Does GraphQL only work over HTTP POST?

By specification GraphQL is transport-agnostic; in practice it is served at a single HTTP endpoint — conventionally /graphql — usually via POST with a JSON body, with GET permitted for queries and WebSockets carrying subscriptions. A GraphQL-over-HTTP specification now standardizes these conventions.

When should you NOT use GraphQL?

Simple resource CRUD with uniform clients, read traffic that HTTP and CDN caching could absorb, file-heavy transfer, and small teams without appetite for resolver batching, query-cost limiting, and schema governance. In those cases a well-designed REST API is less machinery for the same result.

How is a GraphQL API versioned?

Convention is versionless, continuous evolution: add fields freely — clients that don't ask for them are unaffected — and retire old ones with the @deprecated directive instead of shipping /v2. The selection-set model is what makes additive change safe by default.

Related terms

Compare with

Further reading

Ready to build your backend?

Start your project on Back4app in minutes — database, auth, APIs, and Cloud Code included. No credit card required.

Written and reviewed by Back4app Engineering, Back4app Engineering · Published 2026-07-24