Code Generation
GraphQL Code Generator reads your schema and your queries and writes typed TypeScript output: React hooks, Vue composables, plain client functions, server resolver signatures. The win: every query is type-safe end to end, and refactors propagate the moment the schema changes. The cost: one extra build step you run on save.
graphql-codegen config for a React + Apollo project
EXAMPLE
// npm i -D @graphql-codegen/cli @graphql-codegen/client-preset \
// @graphql-codegen/typescript-react-apollo
// codegen.ts
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'https://api.example.com/graphql',
documents: ['src/**/*.tsx', 'src/**/*.ts'], // codegen scans for gql\`...\`
generates: {
'src/__generated__/': {
preset: 'client', // modern preset — uses TypedDocumentNode
presetConfig: { fragmentMasking: false },
},
},
ignoreNoDocuments: true, // first run before any queries exist
};
export default config;
// package.json
// {
// "scripts": {
// "codegen": "graphql-codegen",
// "codegen:watch": "graphql-codegen --watch"
// }
// }
// src/queries/orders.ts — write the query inline, get types for free
import { gql } from '../__generated__/gql';
export const OrdersQuery = gql(/* GraphQL */ \`
query Orders($customerId: ID!, $status: OrderStatus = OPEN) {
orders(customerId: $customerId, status: $status) {
id
customer
totalCents
status
createdAt
}
}
\`);
// src/components/OrdersList.tsx — the hook is FULLY TYPED
import { useQuery } from '@apollo/client';
import { OrdersQuery } from '../queries/orders';
export function OrdersList({ customerId }: { customerId: string }) {
const { data, loading, error } = useQuery(OrdersQuery, {
variables: { customerId, status: 'OPEN' }, // status is the GENERATED enum
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data?.orders.map((o) => (
<li key={o.id}>{o.customer} — ${(o.totalCents / 100).toFixed(2)}</li>
))}
</ul>
);
}
// src/mutations/cancel.ts
export const CancelOrder = gql(/* GraphQL */ \`
mutation CancelOrder($id: ID!) {
cancelOrder(id: $id) { id status }
}
\`);
// Pre-commit hook (Husky) — verify schema sync and types
// .husky/pre-commit
// npm run codegen --silent
// npx tsc --noEmit
// Server-side: schema-first resolvers with TYPED signatures
// generates:
// src/__generated__/resolvers.ts:
// plugins: ['typescript', 'typescript-resolvers']
// config: { contextType: '../context#AppContext' }
//
// Then:
// const resolvers: Resolvers = {
// Query: { orders: async (_, { customerId, status }, ctx) => { ... } },
// Mutation: { cancelOrder: async (_, { id }, ctx) => { ... } },
// };
// The compiler now ensures every resolver matches the schema.
Why it matters
Run codegen in --watch during dev and pin it as a CI step. The compiler then enforces the contract: every query change re-types the call sites, every schema change surfaces breaking consumers at the PR stage. Without it, type drift quietly accumulates and bites you on a Friday deploy.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
# graphql-code-generator turns your schema into TS types. npm i -D @graphql-codegen/cli npx graphql-codegen initTry it Yourself »
Discussion
Loading…