iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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 init
Try it Yourself »

Discussion

Loading…