Understanding GraphQL SDL → TS
The schema is the contract, TypeScript is the safety net.
What GraphQL Code Generator produces, why typed queries beat any runtime client, and the pitfalls of resolver typing.
SDL — the schema language.
GraphQL Schema Definition Language is a small, declarative format. Types, queries, mutations, subscriptions. type User { id: ID!, name: String, posts: [Post!]! }. Question mark missing = required; exclamation = required output. Single source of truth for clients and servers. SDL is intentionally narrow — no opinions about transport, auth, or resolver semantics; just shape.
What gets generated.
For each type: a TypeScript interface. For each query string in your codebase: a typed function whose result is exactly the fields you asked for. For each mutation: a typed mutate function. The result is that useQuery(GetUserQuery, { id })returns { data: { user: { id: string, name: string | null } } } — fields you didn't request aren't in the type. GraphQL Code Generator + the operations plugin is the dominant toolchain.
A worked query.
Schema: type Query { user(id: ID!): User } type User { id: ID! name: String email: String }. Query: query GetUser($id: ID!) { user(id: $id) { id name } }. Generated TS: type GetUserQuery = { user: { id: string, name: string | null } | null }. Email field isn't there — the generator only includes fields the operation asks for. The component that uses the query is statically safe against typos and against renames in the schema.
GetUser query
SDL + operation → typed result
Only the fields you asked for.
user.id + user.name ; email absent from type
= Exact typing
Fragments and refetching.
GraphQL fragments are reusable field selections. fragment UserCard on User { id, name, avatarUrl }can be embedded in any query. Codegen produces a fragment type that components can accept as a prop. The pattern decouples "what does this component need" from "which query fetches it" — every parent that uses UserCard adds the fragment to its own query. Apollo and urql both support it natively.
Server-side typing.
The same codegen produces resolver types on the server. Each resolver is(parent, args, ctx, info) => result with types derived from the SDL. GraphQL's "object-then-resolver" model means N+1 queries are easy to write accidentally; DataLoader-style batching is the canonical fix. The generator can't prevent the N+1 bug; it does ensure the resolver shapes line up with the schema.
vs OpenAPI + tRPC.
GraphQL: clients pick which fields they need, single endpoint, runtime introspection. Costs: more server complexity, harder caching at the HTTP layer. OpenAPI + REST: predictable URLs, easy HTTP cache, more codegen ceremony. tRPC: TypeScript-end-to-end, no IDL at all, but only works within a single TS codebase. GraphQL is the right answer when consumers are heterogeneous and demand differs across them; the codegen tooling is what makes it bearable to use.