The query language Facebook built to escape REST’s over-fetching problem, why it caught on for cross-platform apps, the N+1 trap, and the honest answer to whether a solo SaaS founder should adopt it.
Research-based overview. This article synthesizes the official GraphQL specification, Apollo’s and Relay’s published documentation, the GraphQL Foundation’s state-of-the-ecosystem reports, and public adoption patterns from Hasura, PostGraphile, and GraphQL Yoga. How we research.
GraphQL is a piece of API technology everyone has heard of, fewer have built with, and even fewer have built with at a scale where the trade-offs become real. The public conversation is dominated by people running it at large companies with cross-platform engineering teams — where the rationale is genuinely compelling. It rarely addresses what GraphQL looks like in a single-developer Next.js codebase, which is where most solo SaaS work happens. The short answer for that audience is “probably not GraphQL,” but the long answer is more interesting.
Facebook’s engineers in the early 2010s had a specific pain. They were rebuilding the Facebook mobile app from a HTML5 wrapper to native iOS and Android. Native apps needed different shapes of data than the web app — a mobile newsfeed cell shows different fields than a web feed item, profile screens differ across iOS, Android, and web.
With REST, every screen needed a specific endpoint, or the client had to call multiple endpoints and stitch results together. Both options scaled badly: backend teams built dozens of near-duplicate endpoints, and mobile clients made too many requests over flaky networks.
GraphQL was the answer. A single endpoint accepts a query describing exactly which fields the client wants. The server returns a response in that shape. Backend teams stop building screen-specific endpoints; clients stop over-fetching.
Consider a user profile screen that needs to show a user’s name and avatar, their last five posts (with title, date, comment count), and the unread notification count.
With a typical REST API, this requires three or four requests:
GET /users/123 — for name and avatar (and a bunch of fields you do not need)GET /users/123/posts?limit=5 — for the last five posts (each post likely includes the full post body even though you only need the title)GET /users/123/notifications/unread-count — for the badge numberWith GraphQL, this is a single request:
The server returns precisely the fields requested, nothing more. No over-fetching, no waterfall of network requests, one round-trip instead of three. On a slow mobile network, the difference between one round-trip and three is the difference between a screen that feels instant and a screen that feels broken.
GraphQL splits operations into three categories:
Each operation type has different infrastructure requirements. Queries and mutations work over plain HTTP. Subscriptions need a persistent connection layer that is more expensive to operate. Many SaaS apps use GraphQL for queries and mutations and reach for a different tool (Pusher, Ably, Supabase Realtime) for the subscription layer.
You define a type schema in SDL (Schema Definition Language) that describes every type, field, and operation your API exposes. The schema is the contract between client and server.
A minimal schema for the profile example:
The tooling ecosystem generates TypeScript types, mock servers, query explorers (GraphiQL, Apollo Sandbox), and docs from the schema. The schema is the single source of truth — when it is right, every downstream artifact stays in sync automatically.
For a solo founder evaluating GraphQL today: Hasura or PostGraphile is lowest-friction if you already have Postgres; Apollo or Yoga is the right pick if you want to write the schema by hand.
GraphQL’s biggest operational gotcha is the N+1 problem. Consider a query that asks for a list of posts and the author of each post:
A naive resolver does one query for the posts (the “1”), then one query per post for the author (the “N”). For 20 posts, 21 database queries. At scale, this kills the database.
The standard solution is DataLoader, a batching-and-caching utility from Facebook. It collects all author-id lookups within a single execution, batches them into one query (WHERE id IN (...)), and caches within the request lifecycle. With DataLoader the query becomes two calls: one for posts, one batched call for all authors.
The trap: DataLoader is not automatic. You configure it for every relation that might trigger N+1, which is most of them. New developers on a GraphQL codebase routinely ship N+1 bugs because they did not know DataLoader existed for that field.
REST caches trivially: every endpoint is a URL, CDNs handle the rest. GraphQL throws this away because every request is a POST to the same /graphql endpoint. CDN-level HTTP caching does not work out of the box.
The workarounds:
None of this is fatal, but it is meaningfully more complex than REST + CDN. For founders who want HTTP caching to mostly just work, GraphQL is a step backward.
REST authorization is endpoint-level: check permissions, return 403, done. GraphQL has to authorize at the field level because a single query can touch many resources. { user(id: 1) { posts { author { email } } } } requires checks at each field. Libraries like GraphQL Shield and Apollo’s permission directives exist, but it takes more careful design than REST’s endpoint-level checks. Permission bugs in GraphQL apps often manifest as “a field that should have been gated was not.”
GraphQL is JSON-only and does not handle binary data natively. Two workable patterns:
GET /api/projects, POST /api/projects, PATCH /api/projects/:id. REST is faster to build and easier to debug at small scale.The three options solo SaaS founders actually pick between:
| Option | Strengths | Trade-offs |
|---|---|---|
| REST | HTTP-native, widely understood, easy CDN caching, no extra runtime | Fixed response shapes, over-fetching on rich screens, multiple round-trips for nested data |
| GraphQL | Flexible queries, schema-first, single round-trip for nested data, strong tooling | N+1 risk, harder caching, harder authorization, file uploads awkward, more concepts to learn |
| tRPC | End-to-end TypeScript types, no schema duplication, fastest DX in a Next.js monorepo | TypeScript-only, no third-party clients, no GET caching for queries by default, smaller ecosystem |
For most solo SaaS founders building a Next.js app where client and server share a TypeScript codebase, tRPC beats GraphQL. Every server procedure is automatically typed on the client, refactors propagate end-to-end, and there is no schema, no codegen, no second language. tRPC ate the role GraphQL was reaching for in the TypeScript ecosystem — not because GraphQL is worse, but because the stack going all-TypeScript solved the client-server type-contract problem a different way.
GraphQL still wins for the multi-platform case, the public-API case, and federation — but those are not the cases most solo founders are in.
Do not adopt GraphQL by default. The complexity is real, the footguns are real, and the wins do not materialize until you have multiple clients with diverging data needs or a federated services architecture. For a single Next.js app with a single Postgres backend, REST or tRPC moves faster and breaks less often.
Reach for GraphQL when you have a mobile + web + partner-API surface, when your relational data model needs traversal flexibility on the client, or when you are joining a codebase that already runs GraphQL.
If you do go GraphQL: learn DataLoader early, plan authorization before you ship, and decide consciously whether you need subscriptions or whether queries and mutations are enough. Related infrastructure trade-offs are covered in what is a CDN, SaaS database schema patterns, what is an ORM, Supabase RLS for multi-tenant SaaS, what is a JWT, and what is a webhook. The best SaaS tools for developers roundup places each in a modern stack.
One sentence: GraphQL is a powerful tool for a specific class of problems most solo SaaS founders do not have. REST for simple cases, tRPC for TypeScript monorepos, GraphQL when the multi-client or relational-graph nature of your problem makes the trade-off worth paying.
The stack, prompts, pricing, and mistakes to avoid — for solo founders building with AI.