Apollo 17: Schema-Driven Development
easy⏱ 5 mincourseapollo
Schema-first approach
In schema-driven development, you design your GraphQL schema before writing any resolver or component code. The schema becomes the contract between backend and frontend teams. Once defined, tools like @graphql-codegen read your schema and operations (.graphql files) to generate:
- TypeScript types for every type, input, and enum in your schema.
- Typed document nodes that replace
gql tagged templates.
- Typed hooks (
useGetUsersQuery, useAddTodoMutation) with full input/output typing.
This eliminates manual type definitions and catches schema mismatches at build time, not runtime.
# schema.graphql
type User {
id: ID!
name: String!
email: String!
role: Role!
posts: [Post!]!
}
enum Role {
USER
ADMIN
MODERATOR
}
type Post {
id: ID!
title: String!
body: String!
author: User!
createdAt: DateTime!
}
type Query {
users: [User!]!
user(id: ID!): User
posts(limit: Int, offset: Int): [Post!]!
}
type Mutation {
createPost(input: CreatePostInput!): Post!
updateUser(id: ID!, input: UpdateUserInput!): User!
}
Write a schema and generate types
Write a GraphQL schema with User, Post, and Comment types. Define Query and Mutation root types. Create a codegen.ts config file that uses @graphql-codegen/typescript, @graphql-codegen/typescript-operations, and @graphql-codegen/typescript-react-apollo plugins. Point documents at your .graphql files and schema at your SDL. Run codegen and use the generated useGetUsersQuery hook in a component.
TypedDocumentNode
TypedDocumentNode is a typed version of DocumentNode that carries the query's result and variable types. Instead of gql tagged templates (which return untyped DocumentNode), codegen produces typed documents. When you pass a TypedDocumentNode<TData, TVars> to useQuery, the return type is automatically QueryResult<TData, TVars> — no manual generic annotations needed. This is the foundation of zero-config type safety with Apollo.
// Generated by codegen — fully typed!
import { TypedDocumentNode } from '@apollo/client';
type GetUsersQuery = {
users: Array<{ id: string; name: string; email: string }>;
};
const GetUsersDocument: TypedDocumentNode<GetUsersQuery, {}> = gql`
query GetUsers {
users { id name email }
}
`;
// useQuery infers types automatically
const { data } = useQuery(GetUsersDocument);
// data.users is typed as Array<{ id: string; name: string; email: string }>
End-to-end type safety
With schema-driven development, types flow from schema → codegen → hooks → components without any manual type definitions. When the schema changes (e.g., a field is renamed), codegen produces updated types, and TypeScript immediately flags every component that uses the old field name. This catches breaking changes at build time. Combined with CI/CD, schema changes become safe: if the build passes, the frontend is compatible with the new schema.