GraphQL Code-First vs Schema-First
There are two main ways to build a GraphQL schema: write the SDL (Schema Definition Language) first and generate code from it, or write code first and generate the SDL from it. Both approaches produce the same working API. The difference is where you spend your time and what you maintain as the source of truth.
Schema-First Approach
You write the SDL in a .graphql file. This SDL is the source of truth. Tools generate TypeScript types from it. Resolvers are written separately and must match the schema manually.
Workflow:
──────────
1. Write SDL in schema.graphql
2. Run code generator → TypeScript types
3. Write resolvers that match the schema
schema.graphql:
────────────────
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book!]!
}
index.ts (resolvers):
─────────────────────
const resolvers = {
Query: {
books: (): Book[] => db.books.findAll()
}
};
Code-First Approach
You write classes or objects in TypeScript. The framework generates the SDL automatically. TypeNexus and Pothos are popular code-first frameworks for Node.js.
Workflow:
──────────
1. Write type definitions in TypeScript
2. Framework generates SDL automatically
3. No separate .graphql files to maintain
Using Pothos (code-first):
───────────────────────────
import SchemaBuilder from '@pothos/core';
const builder = new SchemaBuilder({});
const BookType = builder.objectType('Book', {
fields: (t) => ({
id: t.exposeID('id'),
title: t.exposeString('title'),
author: t.exposeString('author'),
})
});
builder.queryType({
fields: (t) => ({
books: t.field({
type: [BookType],
resolve: () => db.books.findAll(),
})
})
});
// Generated SDL (automatic):
// type Book { id: ID! title: String! author: String! }
// type Query { books: [Book!]! }
Side-by-Side Comparison
Aspect Schema-First Code-First ────── ──────────── ────────── Source of truth .graphql SDL file TypeScript code Type safety Via code generator Built-in from types SDL file Written manually Generated automatically Learning curve Familiar SDL syntax Framework-specific API Refactoring Update SDL + types Update code only Team collaboration Non-devs can read Devs only Large schema Can become unwieldy Code scales better Best for API design-first Developer-first teams
When to Choose Schema-First
Schema-first works well when API designers, product managers, or frontend developers collaborate on the schema before any backend code is written. The SDL is human-readable and easy to review in pull requests. It also fits teams using GraphQL Code Generator heavily.
When to Choose Code-First
Code-first works well when the backend team builds the schema iteratively alongside the business logic. Because TypeScript types and GraphQL types live in the same file, there is no risk of them drifting out of sync. Pothos and TypeGraphQL are popular choices.
Hybrid Approach
Many large projects start schema-first for API design reviews, then move to code-first as the implementation grows. The generated SDL from code-first tools can be committed to the repository so API consumers still have a readable schema file to reference.
Key Points
- Schema-first uses SDL as the source of truth; code-first uses TypeScript as the source of truth.
- Schema-first is more collaborative and readable for non-developers; code-first offers stronger type safety with less duplication.
- Both produce an identical, working GraphQL schema at runtime.
- Code generators like GraphQL Code Generator bridge the gap for schema-first teams needing TypeScript types.
