When would you choose GitHub's GraphQL API over its REST API, and what do you give up?
answer
- one endpoint versus many resources
- who decides the response shape
- round trips across relationships
- requests counted versus points charged
- some data lives in only one of them
basics
~20 sChoose GitHub's GraphQL API when one round trip should return exactly the fields you need across related objects, or for surfaces only it exposes, such as Projects v2. You give up simple HTTP caching, several REST-only endpoints, and request-count rate limiting.
solid answer
~50 sGitHub exposes two APIs over the same data. REST is many resource endpoints under `https://api.github.com`, each returning a fixed representation; GraphQL is a single `POST https://api.github.com/graphql` where you declare the exact shape you want. GraphQL wins when the natural question spans relationships — "for these 50 pull requests, give me the author, the review decision, and the last commit's status" is one query instead of a fan-out of REST calls — and it is the only API for some surfaces, notably Projects v2. REST wins when you want one plain resource, when you rely on `ETag`/`If-None-Match` conditional requests, when the endpoint simply has no GraphQL equivalent (large parts of repository and Actions administration), or when you want unauthenticated access, which GraphQL does not allow. The tradeoff people forget is metering: REST counts requests against an hourly budget, GraphQL charges a **point cost** derived from how many nodes a query could return, so a careless query with big `first:` values can cost more than the REST calls it replaced.
code
graphql · 19 linesquery($org: String!, $after: String) {
rateLimit { limit cost remaining resetAt }
organization(login: $org) {
repositories(first: 20, after: $after, orderBy: {field: PUSHED_AT, direction: DESC}) {
pageInfo { hasNextPage endCursor }
nodes {
name
pullRequests(states: OPEN, first: 10) {
nodes {
number
title
reviewDecision
author { login }
}
}
}
}
}
}go deeper
Know that GitHub offers both a REST API of many endpoints and a single GraphQL endpoint where you request specific fields, and that GraphQL can fetch related data in one call.
Explain the concrete differences: cursor connections with pageInfo versus page and per_page, requesting only named fields, the single POST endpoint, and that authentication is mandatory for GraphQL.
Show measured judgment — costing a GraphQL query with the rateLimit field, keeping REST for ETag-backed polling, handling partial errors on a 200, and mixing both APIs where coverage forces it.
Own the guidance for many teams: which workloads belong on which API, how query cost is reviewed before rollout, how schema changes are absorbed, and how client libraries hide the difference from feature teams.
## The two surfaces **REST** lives under `https://api.github.com` as a large set of resource endpoints — `/repos/{owner}/{repo}/pulls`, `/orgs/{org}/members`, and so on. Each returns a server-decided representation. Paging is via `page` and `per_page` (maximum 100), with the next page advertised in the response's `Link` header. **GraphQL** is one endpoint, `https://api.github.com/graphql`, always POST, always authenticated — there is no anonymous GraphQL access. You send a query document describing exactly the fields you want and get back JSON of the same shape. Collections are **connections**, paged with `first:`/`after:` and a `pageInfo { hasNextPage endCursor }` block that you loop on. ## What GraphQL genuinely buys you **Fewer round trips across relationships.** The canonical case is a dashboard: repositories → their open pull requests → each PR's author, labels, review decision, and the status of its head commit. In REST that is one call per repository plus one per pull request plus one per commit status. In GraphQL it is one document. On a slow link or across a large org, the latency difference is the whole story. **Exactly the fields you asked for.** REST issue payloads are large; if you want three fields you still transfer all of them. GraphQL responses contain nothing you did not name. **Access to GraphQL-only data.** Projects v2 — the current GitHub Projects with custom fields and views — is exposed through GraphQL, not REST. Some richer relationships, like a pull request's `reviewDecision`, are natural in GraphQL and awkward or absent in REST. **A typed schema.** The schema is introspectable, so tooling can generate types and validate queries at build time, and a query naming a field that does not exist fails immediately rather than silently returning null. ## What you give up **HTTP caching.** Everything is a POST to one URL, so intermediary caches, `ETag`/`If-None-Match` conditional requests and their quota-free 304s, and CDN behaviour all stop applying. If your workload is "poll something that rarely changes", REST with ETags is cheaper and simpler. **Coverage.** Not every REST endpoint has a GraphQL counterpart. Much of repository administration, Actions, and various settings surfaces exist only in REST. Real integrations very often use both. **Unauthenticated access.** REST allows anonymous reads of public data at a small rate limit. GraphQL requires a token for every call. **Error simplicity.** A GraphQL response can return HTTP 200 with an `errors` array and partial `data`, so a client that only checks the status code will silently treat a half-failed query as success. Clients must inspect the body. **Predictable cost.** See below — this is the one that surprises people. ## Rate limiting is a different model REST spends one request from an hourly budget per call. GraphQL spends **points** from an hourly point budget, computed from the number of nodes the query could return — driven by your `first:`/`last:` arguments across nested connections, not by how many nodes actually came back. Ask for `first: 100` repositories each with `first: 100` pull requests and you are billed for the product, whether or not those objects exist. There is also a hard ceiling on how many nodes one call may request. The practical discipline: include the `rateLimit` field in your query. It returns `limit`, `cost`, `remaining`, and `resetAt` for that very call, so you can measure the cost instead of guessing, and tune page sizes down until the cost is sane. "We moved to GraphQL to save rate limit" is only credible with those numbers in hand. ## Pagination differences that bite REST paging by page number is stable enough for static data but can skip or duplicate items when the underlying list changes between pages. GraphQL's cursor paging is more robust to that, since `endCursor` marks a position rather than an offset. Either way, loop until the API says there is no more — `hasNextPage` in GraphQL, the absence of a next relation in REST — rather than computing a page count up front. ## How to decide Ask three questions: 1. **Does the data I need span relationships?** If yes, GraphQL is likely fewer calls and less latency. 2. **Does the endpoint exist in both?** If a surface is REST-only (much of administration and Actions) or GraphQL-only (Projects v2), the choice is made for you. 3. **Is this a hot polling path over rarely-changing data?** If yes, REST with ETags is cheaper, because 304s are free against the primary limit. The mature answer is not "GraphQL is modern, use it". It is that GitHub ships both because they solve different shapes of problem, and a serious integration usually contains both, with the choice made per workload and backed by measured cost.
- How is GitHub's GraphQL rate limit calculated, and how do you find a query's cost?It is a point budget rather than a request count, and the cost is derived from how many nodes the query could return — the product of your first and last arguments across nested connections, not the number actually returned. Include the rateLimit field in the query itself; the response reports limit, cost, remaining and resetAt for that call, so you can tune page sizes against real numbers.
- Why can a GraphQL call return HTTP 200 and still have failed?GraphQL reports field-level problems in an errors array in the body, often alongside partial data, while the transport status stays 200. A client that only checks the status code will treat a partially failed response as complete. Always inspect errors before using data, and decide per field whether a null is acceptable or should abort the operation.
- Which caching advantage does REST keep that GraphQL loses?Conditional requests. REST responses carry an ETag you can send back as If-None-Match; an unchanged resource returns 304, which costs no primary rate limit and almost no bandwidth. GraphQL is a POST to a single URL, so that mechanism and ordinary HTTP intermediary caching do not apply, and you must cache in your own layer instead.
- Would you build an integration on only one of the two?Rarely. Coverage is uneven in both directions — Projects v2 is GraphQL-only while much of repository and Actions administration is REST-only — so most real integrations use GraphQL for relationship-heavy reads and REST for administration, single-resource fetches and cheap conditional polling. Pick per workload rather than declaring a house standard.
saying these in an interview costs you the question
- Claims GraphQL always uses less rate limit
- Assumes every REST endpoint has a GraphQL equivalent
- Checks only the HTTP status of a GraphQL response
- Thinks GraphQL supports unauthenticated public reads
- Believes ETag caching works the same for both APIs