What is the difference between schema-first and code-first GraphQL schema construction?
answer
- Same destination, two different sources
- Which artefact does a human edit?
- One is parsed in, one is printed out
- The specification takes no side here
- Drift versus a contract nobody wrote
basics
~20 sSchema-first means a hand-written SDL document is the source of truth and the server binds code to it. Code-first means the schema is built from server-side type declarations and printed to SDL afterwards, if at all.
solid answer
~40 sBoth approaches end at the same place: an executable schema in the server's memory that answers introspection and validates documents. They differ in which artefact a human edits. **Schema-first**: you write SDL by hand, commit it, and the server parses it and binds each field to code at startup — the SDL is the contract and the code must match it. **Code-first**: you declare types in the server's own language (classes, builders, annotations), the server assembles the schema from those declarations, and SDL is an *output* you print rather than an input you edit. The GraphQL specification takes no side; it defines the type system, an SDL grammar for describing it, and introspection, but says nothing about how a server constructs its schema. A client cannot tell which approach was used.
code
graphql · 13 linestype Campaign {
id: ID!
title: String!
raisedMinorUnits: Int!
donations(first: Int, after: String): DonationConnection!
}
type Donation {
id: ID!
campaign: Campaign!
amountMinorUnits: Int!
receivedAt: String!
}go deeper
Be ready to state which artefact a person edits in each approach: a hand-written SDL file in schema-first, server-side type declarations in code-first. Add that both end up as the same runnable schema and you have answered the screening version well.
Explain the binding step in schema-first and the assembly step in code-first, and name the tradeoff each carries — drift between two artefacts on one side, an unwritten contract on the other. Mention that printing the schema is how code-first gets a reviewable artefact back.
Show that you have chosen this for a real service. Talk about which side your refactoring tools pull toward, where the API surface becomes visible in review, and what you put in the build so the choice does not quietly cost you a contract.
Own it as a question about who is allowed to change the public surface and when they find out they did. Frame schema-first versus code-first as where you place the review gate for a shared graph, not as a matter of taste in a single service.
## Both roads end at the same executable schema A GraphQL server, whatever language it is written in, holds one thing at runtime: an **executable schema**. That is the in-memory type system — object types, their fields, each field's type and arguments, the interfaces and unions, the enums and input objects — together with the code that produces a value for each field. Every incoming document is validated against it, every introspection query is answered from it, and every response is coerced through it. Schema-first and code-first are two ways of *arriving* at that object. They are a development-workflow choice, not a protocol choice. Nothing in the wire format changes; a consumer introspecting the endpoint sees exactly the same type system either way. ## Schema-first: the SDL document is the source of truth In schema-first you write the Schema Definition Language by hand and commit it, the way you would commit any other checked-in interface description: ```graphql type Campaign { id: ID! title: String! raisedMinorUnits: Int! donations(first: Int, after: String): DonationConnection! } ``` At startup the server parses that text into a type system, then **binds** implementation to it: for each field that needs custom logic it looks up a function, method or handler and attaches it. Fields with no custom logic fall back to whatever default resolution the server provides. The consequences follow from the fact that the contract is a file: * It is readable by anyone — a front-end engineer, a partner team, a reviewer — without reading server code. * It reviews like a document. A pull request that adds a field shows the added field, in the language of the API. * It can be authored *before* the implementation exists, and it can be authored by someone who will not write the resolvers. * But the binding between the SDL and the code is a second, unchecked relationship. Both artefacts can be edited independently, and keeping them consistent is work. ## Code-first: the schema falls out of server types In code-first you never write SDL as an input. You declare the types in the server's own language — a class per object type, a method or property per field, with the language's own types carrying the GraphQL types — and the server library walks those declarations and builds the schema. The shape of the declaration is library-specific; the *idea* is not: ```pseudocode objectType("Campaign") { field("id", nonNull(ID)) field("title", nonNull(String)) field("raisedMinorUnits", nonNull(Int)) field("donations", nonNull(ref("DonationConnection"))) .arg("first", Int) .arg("after", String) .resolvedBy(loadDonationsPage) } ``` The consequences again follow from the artefact: * There is exactly one place a field exists, so the schema and the implementation cannot disagree — a field with no implementation is a compile error, not a null at runtime. * Refactoring tools understand it. Renaming, extracting and moving work the way they do in the rest of the codebase. * But the contract is now *implied*. To read the API surface you either run the server and introspect it, or run a print step. Nobody wrote the contract, so nobody reviewed it as a contract — a point that becomes an organisational problem at scale rather than a technical one. ## Printing the schema closes part of the gap Most code-first setups add a build step that prints the assembled schema to an SDL file and commits it. That file is an **output**, not a source: editing it changes nothing. Its purpose is to give the API surface a location — something a reviewer can read, something whose diff appears in a pull request, something a consumer can be handed. This is the standard way a code-first codebase recovers the one thing schema-first gets for free. A detail worth knowing: printing from the *executable schema* and printing from an *introspection result* are not equivalent. Introspection does not expose applications of custom directives, so a schema reconstructed from an introspection result silently loses them. ## What the specification actually says Nothing about this choice. The specification defines the type system, the SDL grammar that describes it, the validation rules and the introspection system. How a server gets from source code to a type system is entirely an implementation matter, which is why both approaches are first-class and why servers in the same language often offer both. ## How to answer in an interview Say which artefact a human edits, say that both produce the same executable schema, and then name the one tradeoff each side owns: schema-first buys a reviewable contract at the cost of a binding that can drift; code-first removes the drift at the cost of a contract nobody explicitly wrote.
- Can a client tell which approach a server used?No. Introspection answers from the executable schema, and that object is identical whether it was parsed from an SDL file or assembled from server-side declarations. The choice is invisible on the wire — it affects who edits what in the repository, not what the protocol carries.
- Does the GraphQL specification require a server to support SDL at all?No. The specification defines the type system, an SDL grammar for describing it, validation and introspection, but it does not require a server to parse SDL at runtime. A server may build its type system entirely in code and still be fully conforming, because conformance is judged by execution and introspection behaviour.
- Can you design the schema in SDL and still implement it code-first?Yes, and it is a common hybrid. The SDL is written first as a design artefact and reviewed as the contract, the implementation is built code-first, and a build step prints the assembled schema and compares it with the designed one. You get the review artefact without the runtime binding step, at the cost of maintaining the comparison.
Schema-first is a building's blueprint, drawn first and then built to. Code-first is a laser scan of the finished building: accurate by construction, but nobody agreed to it in advance.
saying these in an interview costs you the question
- Claiming the GraphQL specification mandates one approach
- Saying a code-first server cannot be introspected
- Thinking clients can detect which approach was used
- Treating a printed SDL file as an editable source
- Calling code-first 'schemaless' because no file is written
- Assuming schema-first means the schema cannot change