This chapter follows Getting started with urql.
String queries in components drift from the schema silently. GraphQL Code Generator reads your schema and operation documents, then emits TypeScript that tracks field types through refactors.
Install
pnpm add -D @graphql-codegen/cli @graphql-codegen/client-presetpnpm add graphqlPoint the schema at your NestJS schema.gql, introspection URL, or a checked-in SDL file.
Configure codegen.ts
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = { schema: "http://localhost:3000/graphql", documents: ["src/**/*.graphql"], ignoreNoDocuments: true, generates: { "./src/gql/": { preset: "client", presetConfig: { fragmentMasking: { unmaskFunctionName: "getFragmentData" }, }, config: { useTypeImports: true, }, }, },};
export default config;Add a script:
"scripts": { "codegen": "graphql-codegen --config codegen.ts", "codegen:watch": "graphql-codegen --config codegen.ts --watch"}Run pnpm codegen whenever schema or documents change; use watch during development.
Write operations in .graphql files
query Books { books { id title }}
mutation CreateBook($input: CreateBookInput!) { createBook(input: $input) { id title }}The client preset emits the graphql() function and typed document constants—no separate @graphql-codegen/typescript-urql plugin is required when you call urql with generated documents.
Use generated documents with urql
import { useQuery } from "urql";import { graphql } from "../../gql";
const BooksDocument = graphql(` query Books { books { id title } }`);
export function BooksList() { const [result] = useQuery({ query: BooksDocument }); // result.data is typed}Import graphql only from the generated folder so the compiler validates operations.
Fragment masking
Colocate fragments and unwrap them with FragmentType and getFragmentData (or your configured unmask name):
fragment BookSummary on Book { id title}import { type FragmentType, getFragmentData } from "../../gql";import { graphql } from "../../gql";
const BookSummaryFragment = graphql(` fragment BookSummary on Book { id title }`);
type Props = { book: FragmentType<typeof BookSummaryFragment> };
export function BookCard({ book }: Props) { const data = getFragmentData(BookSummaryFragment, book); return <span>{data.title}</span>;}Masking prevents parent components from relying on fields they did not spread.
Persisted documents (optional)
For trusted documents / APQ-style allowlists, add a plugin output that hashes operations:
generates: { "./src/gql/": { preset: "client" }, "./persisted-documents.json": { plugins: ["persisted-operations"], },}Upload hashes to the gateway so only known operations execute in production—a security pattern covered later in this series.