Which pagination arguments does the GraphQL specification itself define for a list field?
answer
- The specification is smaller than you think
- Type system, documents, execution — and stopping there
- The connection shape lives in another document
- Just arguments a schema author declared
- Nothing reserved about first or offset
basics
~20 sNone. The GraphQL specification defines the type system, documents and execution, not pagination. first and after come from the Relay cursor connections convention; offset and limit are ordinary field arguments a schema author declares and a resolver implements.
solid answer
~50 sNone. The GraphQL specification covers the type system, document syntax, validation, execution and the response format; it says nothing about slicing a list, and neither does the GraphQL over HTTP specification. Every pagination argument you have seen — `first`, `after`, `offset`, `limit`, `page` — is an ordinary field argument someone declared in SDL and implemented in a resolver. `first`/`after` with a `Connection`/`Edge`/`PageInfo` shape look official because they come from the **Relay server specification**, the cursor connections convention: a separate, very widely adopted document, not part of GraphQL. So `animals(offset: Int, limit: Int): [Animal!]!` on a livestock pedigree graph is exactly as spec-compliant as a connection. The real difference is not legality but what each buys: offsets give you page numbers and a jump to page N, connections give you a stable resume point and convention-aware client tooling.
code
graphql · 27 linestype Query {
animals(offset: Int = 0, limit: Int = 25): [Animal!]!
animalsConnection(first: Int, after: String): AnimalConnection!
}
type AnimalConnection {
edges: [AnimalEdge!]!
pageInfo: PageInfo!
}
type AnimalEdge {
cursor: String!
node: Animal!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type Animal {
id: ID!
name: String!
birthDate: String!
}go deeper
Recall the shape of the specification: type system, documents, validation, execution, response format — and no pagination anywhere. Be ready to say that both offset arguments and connections are things a schema author declares.
Be ready to name where the Connection/Edge/PageInfo shape actually comes from, and to explain what each argument set gives up: positions and page numbers on one side, a stable resume point and convention-aware tooling on the other.
An interviewer expects you to notice that validation bounds nothing — page-size clamping and rejecting a negative offset are your resolver's job — and to justify when a schema may reasonably carry both paging idioms.
Own the consistency decision. Two paging idioms in one API is a real tax on every client and every new team; decide it once in a schema style guide, and treat any published argument as a contract regardless of whether a specification blessed it.
## What the specification actually contains The GraphQL specification is a short document with a narrow remit. It defines the **language** (how an executable document is written), the **type system** (objects, interfaces, unions, enums, input objects, scalars, lists, non-null), **introspection**, **validation** rules, the **execution** algorithm, and the **response format**. Read it end to end and you will not find the words `first`, `after`, `offset`, `page`, `Connection`, `Edge` or `PageInfo`. The GraphQL over HTTP specification, which covers media types, methods and status codes, is silent on the subject too. That is not an oversight. A list type such as `[Animal!]!` is a container, not a query. A field returning it returns exactly what the resolver returns — all of it, in whatever order the resolver produced. If you want a window, you have to design one yourself, out of ordinary field arguments. ## So where did the connection shape come from? The `Connection` / `Edge` / `PageInfo` shape with `first`, `after`, `last` and `before` comes from the **Relay server specification** — the cursor connections convention. It is a separate document, written to give a particular class of client something predictable to code against, and it succeeded so thoroughly that a generation of engineers assumes it is part of GraphQL. It is not. A server that never emits a connection is not violating anything, and a validator will never complain. Two consequences follow, and both come up in interviews. First, no tool can tell you your `offset` field is "wrong GraphQL", because there is no rule to break. Second, the parts of the connection world that people quote most confidently are often not even in the connections document — the plural `nodes` shortcut, `totalCount`, and base64 as the cursor encoding are all conventions layered on a convention. ## Offset arguments are ordinary arguments On a livestock pedigree graph, this is a completely legal field: ```graphql type Query { animals(offset: Int = 0, limit: Int = 25, orderBy: AnimalOrder = BIRTH_DATE_ASC): [Animal!]! } ``` It is introspectable, it has defaults, and validation will check that a supplied `limit` is an `Int`. Validation will check nothing else. `limit: 100000` is a perfectly valid document; so is `offset: -1`. The specification has no notion of a maximum page size, so clamping the page size and rejecting a negative offset are your resolver's job and your schema's documented contract. A weak candidate assumes the framework is guarding this; it is not, because there is nothing in the type system that could express the guard. ## What each shape actually buys Since neither is required, the choice is a design decision, and it is worth being able to state crisply: * **Offset arguments** give you positions. That makes "page 7 of 42", numbered page buttons and a jump straight to the middle of a list trivial — none of which the connection shape models at all. They map directly onto any store that offers skip-and-take, and onto upstream sources that only ever exposed a page number. What you give up is a stable anchor: an offset means "position in whatever this ordering produced at this instant", so a page can shift under you. * **Cursor connections** give you an anchor. A cursor names a place in an ordering, so a resume point survives inserts elsewhere in the set. You also get convention-aware tooling for free: normalized client caches that know how to merge `edges` from successive pages, and generated infinite-scroll UI. What you give up is page numbers and, unless you add it yourself, any notion of a total. Nothing stops one schema carrying both. An admin table over the pedigree graph can take `offset`/`limit` while the public browsing feed exposes a connection. The cost of doing that is consistency, not legality — clients now have to learn two paging idioms in one API, which is a real tax, and it is the kind of thing worth settling once in a schema style guide rather than per team. ## Non-specified does not mean non-binding The last point candidates miss: once you ship `offset` and `limit`, they are part of your published schema. Removing an argument, renaming it, or changing its nullability is a breaking change on exactly the same terms as any other schema change, and a registry checking compatibility will flag it. "It was never in the spec" is not an escape hatch from your own contract. ## The interview answer in one line GraphQL specifies no pagination whatsoever; connections are a widely adopted convention from the Relay server specification, and offset arguments are just arguments. Say that plainly, name where the connection shape actually comes from, and then talk about the trade-off rather than about legality. (How expensive a large offset is to *execute* is a question about your data store, not about GraphQL.)
- If neither shape is specified, what actually breaks when a field exposes offset and limit instead of a connection?Nothing in parsing, validation or execution — it is an ordinary field. What you lose is ecosystem: normalized client caches that know how to merge `edges` across pages, generated list and infinite-scroll components, and a stable resume point. What you gain is page numbers and a jump straight to page N, which the connection shape does not model at all.
- Are first and after reserved argument names in GraphQL?No. GraphQL reserves nothing but the double-underscore prefix, which belongs to introspection meta-fields and types. Argument names are entirely yours — `skip` and `take` are just as valid as `offset` and `limit`. The pull to use `first`/`after` is convention and tooling recognition, not a rule.
- Can one field accept both offset-style and cursor-style arguments at once?It is legal, and validation will not stop a client sending both, because they are simply four independent arguments. That makes precedence your problem: the resolver has to define what happens when `after` and `offset` arrive together, and the honest choice is usually to reject the combination as a field error rather than silently pick a winner.
saying these in an interview costs you the question
- Says cursor connections are part of the GraphQL specification
- Calls offset and limit arguments invalid or non-conformant GraphQL
- Believes GraphQL validates that limit is within a sane range
- Assumes first and after are reserved argument names
- Claims the specification requires cursors to be base64
- Thinks removing an unspecified argument is not a breaking change