Kurs
Handskrivna TypeScript-typer för ett GraphQL-API är inaktuella i samma ögonblick någon ändrar schemat.
Säg att en backendutvecklare byter namn på fältet title på typen Course till name. Frontend-koden kompilerar fortfarande eftersom det handskrivna Course-gränssnittet inte har en aning om att något ändrats, så felet dyker upp först vid körning. Verkliga applikationer tenderar att ha dussintals queries, mutationer och fragment, så du kan se hur snabbt det kan eskalera.
GraphQL Code Generator, eller GraphQL Codegen i korthet, läser ditt schema och dina GraphQL-operationer och gör om dem till typer, hjälpfunktioner för queries, klientintegrationer och annat boilerplate som du annars skulle skriva för hand. Den används oftast med TypeScript och populära klienter som Apollo och urql. Ett kommando synkar dina typer igen när schemat ändras.
I den här artikeln guidar jag dig genom hur du sätter upp GraphQL Codegen i ett litet TypeScript-projekt, konfigurerar den, använder de genererade typerna och kör den automatiskt som en del av ditt arbetsflöde.
Hur står sig GraphQL mot REST? Läs vår Graph QL vs REST: En komplett guide för att lära dig skillnaderna och fördelarna.
Vad är GraphQL Codegen?
GraphQL Codegen är ett kommandoradsverktyg som gör om GraphQL-definitioner till applikationskod.
Det är ett open source-projekt från The Guild, och du installerar det som en dev-beroende i ditt projekt. Det kopplar ihop tre saker:
- GraphQL-schema: Kontraktet mellan servern och dess klienter, med alla typer, fält och argument som API:et exponerar
- GraphQL-operationer: De queries, mutationer, prenumerationer och fragment din applikation skickar till API:et
- Genererad kod: Typerna och hjälparna som Codegen skriver baserat på schemat och dina operationer
Schemat berättar för Codegen vad som är möjligt och operationerna berättar vad din applikation efterfrågar.
Codegen kombinerar båda, så varje genererad typ matchar exakt formen på svaret för en operation. Den validerar också varje operation mot schemat. Det omdöpta fältet title från introduktionen skulle stoppa genereringen med ett fel som Cannot query field "title" on type "Course", och du skulle fånga det innan koden ens körs.
Resultatet beror på dina plugins och din konfiguration. Själva Codegen skriver inte TypeScript. Plugins gör det. Till exempel genererar pluginet typescript schematyper, och typescript-operations genererar typer för dina queries och mutationer. Det finns plugins för andra språk också, som Java och C#, men den här artikeln håller sig till TypeScript.
En sak att komma ihåg är att Codegen inte ersätter din GraphQL-server.
Den löser inte queries eller hämtar data. Den läser GraphQL-definitioner under utveckling och skriver filer. Din server och din GraphQL-klient gör resten.
Hur GraphQL Codegen fungerar
Varje Codegen-setup följer samma flöde: schema + GraphQL-operationer -> Codegen-konfiguration -> genererad kod.

Diagram över arbetsflödet för GraphQL Codegen
Detta är grundidén:
-
Tillhandahåll ett schema: Peka Codegen mot en lokal
.graphql-fil eller ett körande GraphQL-endpoint, som Codegen läser via introspektion -
Peka Codegen mot dina operationer: Använd globmönster som
src/**/*.tsså att Codegen hittar queries, mutationer och fragment i din kod -
Konfigurera plugins och utdata-mål: Tala om för Codegen vilka filer som ska skrivas och vilka plugins som genererar var och en
-
Kör Codegen: Kör
npx graphql-codegenför att läsa in schemat, validera dina operationer och skriva ut resultatet -
Använd de genererade artefakterna: Importera de genererade typerna och dokumentnoderna i din applikationskod
Steg 1 till 3 placeras i en konfigfil, vanligtvis codegen.ts. Här är den minsta varianten för ett kurskatalogsprojekt:
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
// Step 1: Where the schema comes from
schema: "schema.graphql",
// Step 2: Where your operations are placed
documents: ["src/**/*.ts"],
// Step 3: What to generate and where to write it
generates: {
"./src/gql/": { preset: "client" },
},
};
export default config;
Presettet client är ett paket av plugins som genererar schematyper, operationstyper och typade dokumentnoder i ett svep. Jag går igenom var och en i avsnitten om setup och konfiguration.
Så sätter du upp GraphQL Codegen
Det bästa sättet att förstå Codegen är att köra det på ett riktigt projekt.
I det här avsnittet bygger du ett litet kurskatalogsprojekt från grunden. Det har ett schema med kurser och instruktörer, en query och en Codegen-konfiguration som gör om queryn till TypeScript-typer. Allt du behöver är Node.js och npm.
Steg 1: Installera GraphQL Code Generator
Skapa en ny mapp och initiera ett npm-projekt:
mkdir course-catalog && cd course-catalog
npm init -y
Codegen behöver ett par paket. Två hamnar i vanliga beroenden, och resten är dev-beroenden eftersom de bara körs under utveckling:
npm install graphql @graphql-typed-document-node/core
npm install -D typescript @graphql-codegen/cli @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node
Detta gör varje paket:
-
graphql: Referensimplementationen av GraphQL för JavaScript, som Codegen använder för att parsa schema och operationer -
@graphql-typed-document-node/core: Tillhandahåller typenTypedDocumentNodesom din genererade kod importerar -
@graphql-codegen/cli: Codegens kommandoradsverktyg -
@graphql-codegen/typescript-operations: Pluginet som genererar typer för dina queries, mutationer och fragment -
@graphql-codegen/typed-document-node: Pluginet som genererar dokumentobjekt med typerna bifogade
Steg 2: Skapa en konfigurationsfil
Codegen läser sina inställningar från en konfigfil i projektroten. Skapa codegen.ts med en tom konfiguration för nu:
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {};
export default config;
Du kan också köra npx graphql-codegen init för att svara på några frågor och få en genererad konfigfil. Jag skriver den för hand här så att du ser vad varje inställning gör.
Steg 3: Ange platser för schema och dokument
Codegen behöver två indata, så vi skapar båda.
Spara först schemat som schema.graphql i projektroten:
enum Level {
BEGINNER
INTERMEDIATE
ADVANCED
}
type Instructor {
id: ID!
name: String!
bio: String
}
type Course {
id: ID!
title: String!
level: Level!
durationHours: Int!
instructor: Instructor!
}
type Enrollment {
id: ID!
course: Course!
studentEmail: String!
}
type Query {
courses(level: Level, limit: Int): [Course!]!
course(id: ID!): Course
}
type Mutation {
enrollStudent(courseId: ID!, studentEmail: String!): Enrollment!
}
Skapa sedan src/operations/courses.graphql med en query som hämtar kurser och deras instruktörer:
query GetCourses($level: Level, $limit: Int) {
courses(level: $level, limit: $limit) {
id
title
level
instructor {
name
}
}
}
Peka nu Codegen mot båda filerna. Nyckeln schema tar sökvägen till schemat, och documents tar ett globmönster som matchar dina operationer:
const config: CodegenConfig = {
schema: "schema.graphql",
documents: ["src/**/*.graphql"],
};
Steg 4: Välj en utdatafil
Nyckeln generates talar om för Codegen var utdata ska skrivas. Varje nyckel i den är en filsökväg:
const config: CodegenConfig = {
schema: "schema.graphql",
documents: ["src/**/*.graphql"],
generates: {
"src/generated/graphql.ts": {},
},
};
Codegen skapar mappen src/generated/ om den inte finns.
Steg 5: Lägg till relevanta plugins
En utdatafil utan plugins är tom. Lägg till de två plugins du installerade:
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "schema.graphql",
documents: ["src/**/*.graphql"],
generates: {
"src/generated/graphql.ts": {
plugins: ["typescript-operations", "typed-document-node"],
},
},
};
export default config;
Om du följt en äldre tutorial har du troligen sett pluginet typescript i den här listan också. Du kan ta bort det nu, eftersom typescript-operations fungerar för sig själv och bara genererar Input-, Enum- och Operationstyper som faktiskt används. Om du lägger båda i samma utdatafil i de nuvarande versionerna kastar TypeScript fel om dubbla deklarationer.
Steg 6: Kör Codegen
Lägg till ett npm-skript så att du slipper komma ihåg kommandot:
npm pkg set scripts.codegen="graphql-codegen"
Kör det sedan:
npm run codegen
Codegen läser in schemat, validerar queryn mot det och skriver ut resultatet:

Resultat från Codegen-körning
Om en operation refererar till ett fält som inte finns i schemat är det här körningen misslyckas, och inga filer skrivs.
Steg 7: Granska den genererade utdatan
Öppna src/generated/graphql.ts. Det här är vad du ser:

Den genererade utdatan
Codegen genererade fyra saker:
-
Level: En strängunion för enumen, genererad endast för att queryn använder den -
GetCoursesQueryVariables: Typen för variablerna du skickar till queryn, där båda är valfria eftersom schemat inte markerar dem som obligatoriska -
GetCoursesQuery: Formen på svaret, med endast de fält som queryn begär -
GetCoursesDocument: Den parsade queryn som en typad dokumentnod, med resultat- och variabletyper bifogade
Titta på GetCoursesQuery igen. Den inkluderar inte durationHours eller instruktörens bio, eftersom queryn inte begär dem. Om du försöker läsa course.durationHours i din kod får du ett kompileringsfel, inte ett undefined vid körning.
Och det är hela uppsättningen – du skriver GraphQL, och Codegen skriver TypeScript.
Hur du konfigurerar GraphQL Codegen
De flesta Codegen-konfigurationer du ser är längre än den ovan, men de byggs av samma fyra inställningar.
Konfigfilen kan vara codegen.ts, codegen.yml eller codegen.json. Jag använder TypeScript för att få autocomplete och typkontroller på själva konfigurationen. Inställningarna är desamma i varje format.
Schema
Nyckeln schema berättar för Codegen var ditt schema finns. Den accepterar ett par källtyper.
En lokal fil eller ett globmönster fungerar när schemat ligger i ditt repo:
schema: "schema.graphql",
// or, for a schema split across files
schema: "schema/**/*.graphql",
En URL fungerar när schemat finns på en körande server. Codegen skickar en introspektionsfråga till endpointen, och du kan skicka headers för autentisering:
schema: [
{
"https://api.example.com/graphql": {
headers: { Authorization: Bearer ${process.env.API_TOKEN} },
},
},
],
URL-alternativet fungerar bara om servern har introspektion aktiverad. Många produktions-API:er stänger av det, så för CI är en schemafil som är incheckad i repot oftast det säkrare valet.
Documents
Nyckeln documents talar om för Codegen var den hittar dina queries, mutationer, prenumerationer och fragment.
Operationer kan ligga i .graphql-filer eller inline i din TypeScript-kod. Codegen hittar inline-operationer när de är inlindade i en gql-tagg eller markerade med en /* GraphQL */-kommentar:
const EnrollStudent = /* GraphQL */
mutation EnrollStudent($courseId: ID!, $studentEmail: String!) {
enrollStudent(courseId: $courseId, studentEmail: $studentEmail) {
id
course {
title
}
}
}
;
För att skanna båda filtyperna, bredda globen. Lägg till ett negerat mönster så att Codegen inte skannar sin egen utdata:
documents: ["src/**/*.{graphql,ts}", "!src/generated/**"],
Ge varje operation ett unikt namn. Codegen bygger typnamn från operationsnamn, så EnrollStudent blir EnrollStudentMutation och EnrollStudentMutationVariables.
Generates
Nyckeln generates mappar utdata-sökvägar till vad som går in i dem. Du kan ha så många utdata-mål du behöver, var och en med sina egna plugins.
Säg att du vill ha operationstyper i en fil och fulla schematyper i en annan:
generates: {
"src/generated/graphql.ts": {
plugins: ["typescript-operations", "typed-document-node"],
config: {
useTypeImports: true,
},
},
"src/generated/schema-types.ts": {
plugins: ["typescript"],
},
},
Objektet config inuti ett utdata-mål gäller bara för den filen. Här gör useTypeImports att den genererade filen använder import type, vilket vissa bygguppsättningar kräver. Om du placerar config i filens rot gäller det för varje utdata.
En utdata-sökväg kan också vara en mapp som slutar med ett snedstreck, som "src/gql/". Det är formatet som presets använder, eftersom de skriver mer än en fil.
Plugins
Plugins avgör vad som genereras. Codegen laddar schemat och dokumenten, och varje plugin gör om dem till en del av utdatan.
Det här är de plugins du oftast ser i TypeScript-projekt:
-
typescript-operations: Typer för dina queries, mutationer, prenumerationer och fragment, plus de enumar och inputs de använder -
typed-document-node: Dokumentobjekt med resultat- och variabletyper bifogade, som de flesta GraphQL-klienter förstår -
typescript: Typer för varje objekt, enum och input i schemat, oavsett om dina operationer använder dem eller inte -
typescript-resolvers: Typsignaturer för resolver-funktioner på serversidan
Ett preset är ett paket av plugins med en rimlig standardkonfiguration. Presettet client från avsnittet Hur det fungerar är det som The Guild rekommenderar för klientappar. Det bygger på den förbättrade typescript-operations under huven, så du får dess fördelar utan extra setup.
Varje plugin har sina egna konfigurationsalternativ, och det finns många. Du behöver inte lära dig dem i förväg. Börja med standardvärdena och slå upp ett alternativ när den genererade utdatan inte matchar vad din kod förväntar sig.
Vad kan GraphQL Codegen generera?
Vad du får ut av Codegen beror på vilka plugins du väljer.
Samma schema och operationer kan ge antingen rena TypeScript-typer eller färdig klientkod. Här är de utdata du oftast använder.
TypeScript-typer
Pluginet typescript genererar en typ för varje objekt, enum och input i ditt schema. För kurskatalogen ser typen Course ut så här:
export type Course = {
__typename?: 'Course';
durationHours: Scalars['Int']['output'];
id: Scalars['ID']['output'];
instructor: Instructor;
level: Level;
title: Scalars['String']['output'];
};
Dessa typer beskriver hela schemat, vilket gör dem användbara på serversidan, i test-fixtures och i hjälpfunktioner som arbetar med kompletta objekt.
Men de passar dåligt för klientkod. En Course-typ säger att varje fält finns, även de som din query aldrig bad om.
Operationstyper
Pluginet typescript-operations genererar två typer per operation: en för resultatet och en för variablerna. Det fungerar likadant för queries och prenumerationer.
Så här ser det ut för en mutation EnrollStudent:
export type EnrollStudentMutationVariables = Exact<{
courseId: string | number;
studentEmail: string;
}>;
export type EnrollStudentMutation = { enrollStudent: { id: string, course: { title: string } } };
Resultattypen matchar selection setet, fält för fält. Variabeltypen är Exact, så TypeScript avvisar extra eller felstavade variabler.
Variabeln courseId accepterar både string och number eftersom GraphQLs ID-scalar accepterar båda som input. Sedan v6 är standardtypen string | number, vilket är korrekt typ för klientfall.
Typade dokumentnoder
En vanlig DocumentNode är en parsad GraphQL-query. Den känner till queryns struktur, men har ingen aning om vad queryn returnerar eller vilka variabler den tar.
En typad dokumentnod har båda typerna med sig. Pluginet typed-document-node genererar en per operation:
export const GetCoursesDocument = { /* ... */ } as unknown as DocumentNode<GetCoursesQuery, GetCoursesQueryVariables>;
På så sätt kan varje funktion som accepterar en TypedDocumentNode härleda resultat- och variabletyper från dokumentet ensamt. Du skickar inte generics för hand, och du kan inte kombinera en query med fel typer.
De flesta moderna GraphQL-klienter accepterar typade dokumentnoder, inklusive Apollo Client, urql och graphql-request. Du ser alla tre i praktiken längre fram i artikeln.
Klientspecifika integrationer
Vissa plugins går längre och genererar kod för en specifik klient eller ramverk. Till exempel:
-
typescript-react-apollo: Genererar typade React-hooks för Apollo Client -
typescript-urql: Genererar typade hooks och komponenter för urql -
typescript-react-query: Genererar typade hooks för TanStack Query -
typescript-graphql-request: Genererar ett typat SDK med en funktion per operation
Alla dessa är community-plugins. The Guild flyttade dem till ett separat community-repo, så deras releaseschema skiljer sig från kärnpluginsen.
Du behöver dem dessutom mer sällan än förr. När en klient accepterar typade dokumentnoder tillför genererade hooks inte mycket extra typesäkerhet. Apollos egna docs rekommenderar till exempel att använda pluginet typescript-operations, som fokuserar på att bara generera typer och inte inkluderar ytterligare körningskod.
Generellt bör du börja med operationstyper och typade dokumentnoder. Lägg sedan till ett klientspecifikt plugin bara om ditt team vill ha de genererade hooksen eller SDK-funktionerna.
GraphQL Codegen med TypeScript
Genererade typer spelar roll bara om de fångar misstag, så låt oss göra några.
Det här exemplet utökar kurskatalogsprojektet med en andra query, en liten hjälpare som skickar den till API:et och koden som använder resultatet. Det förutsätter en GraphQL-server som implementerar schemat och kör på http://localhost:4000/graphql.
Här är den del av schemat som exemplet använder:
type Instructor {
id: ID!
name: String!
bio: String
}
type Course {
id: ID!
title: String!
level: Level!
durationHours: Int!
instructor: Instructor!
}
type Query {
course(id: ID!): Course
}
Två detaljer spelar roll här. Fältet course returnerar Course utan !, så det är null när ingen kurs matchar ID:t. Instruktörens bio är också nullable.
Skapa src/operations/course.graphql med en query som hämtar en kurs:
query GetCourse($id: ID!) {
course(id: $id) {
title
durationHours
instructor {
name
bio
}
}
}
Kör npm run codegen igen. Codegen lägger till två nya typer i src/generated/graphql.ts:

Innehåll i filen graphql.ts
Nullbarheten från schemat följer med. course kan vara null, bio kan vara null, och id är obligatoriskt eftersom queryn deklarerar det som ID!.
Nu till applikationskoden. Skapa src/execute.ts med en hjälpare som skickar valfritt typat dokument till API:et:
import { print } from "graphql";
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
const API_URL = "http://localhost:4000/graphql";
export async function execute<TResult, TVariables>(
document: TypedDocumentNode<TResult, TVariables>,
variables: TVariables,
): Promise<TResult> {
const response = await fetch(API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: print(document), variables }),
});
const { data, errors } = await response.json();
if (errors?.length) {
throw new Error(errors[0].message);
}
return data as TResult;
}
Hjälparen vet ingenting om kurser. Den får båda typerna från dokumentet du skickar in, och funktionen print() gör om dokumentet tillbaka till en querysträng för begäran.
Använd den sedan i src/course-details.ts:
import { execute } from "./execute";
import { GetCourseDocument, type GetCourseQuery } from "./generated/graphql";
function formatCourse(course: NonNullable<GetCourseQuery["course"]>): string {
const bio = course.instructor.bio ?? "No bio yet";
return course.title({course.durationHours}h) by ${course.instructor.name} - ${bio};
}
const { course } = await execute(GetCourseDocument, { id: "c1" });
if (course) {
console.log(formatCourse(course));
} else {
console.log("Course not found");
}
Funktionen formatCourse() tar sin parametertyp från den genererade querytypen. NonNullable tar bort null-fallet, eftersom funktionen bara körs efter if-kontrollen.
Exemplet använder top-level await, så sätt "type": "module" i package.json och kör det med npx tsx src/course-details.ts:

Lyckat anrop till en mockad GraphQL-server
Nu förutsätter detta att du har en GraphQL-server igång. Här är en mock jag använde:
import { createServer } from "node:http";
import { readFileSync } from "node:fs";
import { createSchema, createYoga } from "graphql-yoga";
const instructors = [{ id: "i1", name: "Dario Radecic", bio: "Developer" }];
const courses = [
{ id: "c1", title: "Intro to GraphQL", level: "BEGINNER", durationHours: 4, instructor: instructors[0] },
{ id: "c2", title: "GraphQL Schema Design", level: "ADVANCED", durationHours: 6, instructor: instructors[0] },
];
const schema = createSchema({
typeDefs: readFileSync("schema.graphql", "utf8"),
resolvers: {
Query: {
courses: (_: unknown, args: { level?: string; limit?: number }) =>
courses
.filter((course) => !args.level || course.level === args.level)
.slice(0, args.limit ?? undefined),
course: (_: unknown, args: { id: string }) =>
courses.find((course) => course.id === args.id) ?? null,
},
Mutation: {
enrollStudent: (_: unknown, args: { courseId: string; studentEmail: string }) => ({
id: "e1",
course: courses.find((course) => course.id === args.courseId),
studentEmail: args.studentEmail,
}),
},
},
});
createServer(createYoga({ schema })).listen(4000, () => {
console.log("Mock API running at http://localhost:4000/graphql");
});
Försök nu att bryta det. Öppna src/course-details.ts och ersätt allt under funktionen formatCourse() med tre misstag som handskrivna typer ofta släpper igenom:
// Missing the required id variable
await execute(GetCourseDocument, {});
const { course } = await execute(GetCourseDocument, { id: "c1" });
// Reading a field the query doesn't request
console.log(course?.level);
// Forgetting that course can be null
console.log(course.title);
Typkontrollera sedan projektet:
npx tsc -p .
TypeScript fångar alla tre innan koden körs:

Fel fångade av TypeScript
Så minskar Codegen mismatchar mellan frontend och API. Typerna kommer från samma schema som servern använder, så de kan inte beskriva fält som inte finns eller missa nulls som servern kan returnera.
Och skyddet slutar inte efter första körningen. Säg att backend-teamet gör durationHours nullable. Du regenererar, typen ändras till number | null, och TypeScript pekar ut varje rad som antog ett nummer.
GraphQL Codegen med GraphQL-klienter
Codegen skickar inte förfrågningar. Det är din GraphQL-klients uppgift.
De genererade typade dokumentnoderna är överlämningspunkten mellan de två. Du skickar dem till klienten, och klienten härleder typerna. Exemplen nedan använder samma fil src/generated/graphql.ts och det mockade API:et från föregående avsnitt, så håll npx tsx mock/server.ts igång i en separat terminal.
GraphQL Codegen med Apollo
Installera Apollo Client och dess peer-beroende rxjs:
npm install @apollo/client rxjs
Exemplet kör också mutationen EnrollStudent. Spara den som src/operations/enroll.graphql:
mutation EnrollStudent($courseId: ID!, $studentEmail: String!) {
enrollStudent(courseId: $courseId, studentEmail: $studentEmail) {
id
course {
title
}
}
}
Apollos dokumentation rekommenderar också två konfigalternativ för Codegen, eftersom Apollo Client alltid inkluderar __typename-fält men inte lägger till dem i rottyper. Lägg till dem i utdata-målet i codegen.ts:
"src/generated/graphql.ts": {
plugins: ["typescript-operations", "typed-document-node"],
config: {
nonOptionalTypename: true,
skipTypeNameForRoot: true,
},
},
Med dessa alternativ får varje objekt i de genererade resultattyperna ett obligatoriskt __typename-fält, som __typename: 'Course'. Det matchar vad Apollos cache returnerar.
Kör npm run codegen igen för att upptäcka den nya mutation och konfigurationen.
Apollo Client accepterar typade dokumentnoder i query() och mutate(). Spara detta som src/apollo.ts:
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { EnrollStudentDocument, GetCoursesDocument } from "./generated/graphql";
const client = new ApolloClient({
link: new HttpLink({ uri: "http://localhost:4000/graphql" }),
cache: new InMemoryCache(),
});
const { data } = await client.query({
query: GetCoursesDocument,
variables: { level: "BEGINNER", limit: 5 },
});
data?.courses.forEach((course) => console.log(course.title));
const result = await client.mutate({
mutation: EnrollStudentDocument,
variables: { courseId: "c1", studentEmail: "student@example.com" },
});
console.log(result.data?.enrollStudent.course.title);
Objektet data är typat som GetCoursesQuery | undefined, och TypeScript kontrollerar variables mot GetCoursesQueryVariables. Mutationen fungerar på samma sätt med EnrollStudentMutation och dess variabler.
Kör det med npx tsx src/apollo.ts:

Apollo-utdata
Den första raden kommer från queryn, eftersom det är den enda nybörjarkursen. Den andra kommer från mutationen, som returnerar titeln på kursen som studenten skrev in sig på.
React-hooks som useQuery() och useMutation() härleder typer från typade dokumentnoder på samma sätt.
GraphQL Codegen med urql
Installera urqls kärnpaket:
npm install @urql/core
urql fungerar med typade dokumentnoder direkt. Skicka dokumentet som första argument till query() i src/urql.ts:
import { Client, cacheExchange, fetchExchange } from "@urql/core";
import { GetCoursesDocument } from "./generated/graphql";
const client = new Client({
url: "http://localhost:4000/graphql",
exchanges: [cacheExchange, fetchExchange],
});
const result = await client
.query(GetCoursesDocument, { level: "ADVANCED" })
.toPromise();
if (result.error) {
throw result.error;
}
result.data?.courses.forEach((course) => console.log(course.title));
Det andra argumentet är typat som queryns variabler, och result.data är typat som queryns resultat.
Kör det med npx tsx src/urql.ts:

Urql-utdata
Ramverksbindningarna, som useQuery() i paketet urql för React, accepterar också typade dokumentnoder.
GraphQL Codegen med andra klienter
Du behöver inte en fullfjädrad klient för att få typade resultat.
Klienten graphql-request är en minimal klient som skickar en förfrågan och returnerar datan. Installera den med följande kommando:
npm install graphql-request
Den härleder typer från typade dokumentnoder precis som Apollo och urql. Spara detta som src/graphql-request.ts:
import { request } from "graphql-request";
import { GetCourseDocument } from "./generated/graphql";
const { course } = await request(
"http://localhost:4000/graphql",
GetCourseDocument,
{ id: "c2" },
);
console.log(course?.title);
Kör det med npx tsx src/graphql-request.ts:

GraphQL-request-utdata
Samma idé fungerar med TanStack Query. Den skickar inte GraphQL-förfrågningar själv, så du anropar graphql-request eller hjälparen execute() från föregående avsnitt inuti queryfunktionen, och resultattypen flödar vidare till din kod.
Och om din klient inte stöder typade dokumentnoder alls kan du ändå använda de rena operationstyperna. Importera GetCourseQuery och GetCourseQueryVariables och skicka dem som generics. Det är mer manuellt arbete, men typerna kommer fortfarande från schemat.
Hur du kör GraphQL Codegen automatiskt
Genererade typer hjälper bara om de matchar det aktuella schemat och operationerna.
Om du glömmer att köra Codegen efter en ändring beskriver typerna kod som inte längre finns. Det är samma driftproblem som handskrivna typer har, bara med extra steg. Lösningen är att ta bort det manuella steget från arbetsflödet.
npm-skript
Börja med ett par skript i package.json, så att alla i teamet kör Codegen på samma sätt:
{
"scripts": {
"codegen": "graphql-codegen",
"codegen:watch": "graphql-codegen --watch",
"codegen:check": "graphql-codegen --check",
"prebuild": "npm run codegen",
"build": "tsc -p ."
}
}
Resten av det här avsnittet förklarar vad varje skript är till för.
Watch-läge
Watch-läget kör om Codegen varje gång en bevakad fil ändras. Det behöver paketet @parcel/watcher, som Codegen inte installerar åt dig:
npm install -D @parcel/watcher
npm run codegen:watch
Om du hoppar över installationen kör Codegen en gång och skriver Failed to import @parcel/watcher istället för att övervaka.
Watch-läget spårar både dina operationer och en lokal schemafil. Om du lägger till ett fält i course.graphql dyker det nya fältet upp i GetCourseQuery en sekund senare. Om du gör durationHours nullable i schema.graphql ändras typen till number | null, och din editor flaggar varje rad som antog ett nummer.
Håll det igång i en terminal bredvid din dev-server under utveckling.
Generera före builds
npm kör ett skript som heter prebuild före build. Med skripten ovan regenererar npm run build typerna först och kör sedan TypeScript-kompilatorn.
På så sätt körs en build aldrig mot inaktuella typer, även om någon glömt köra Codegen efter att ha dragit ändringar.
CI/CD
Flaggan --check kör Codegen i torrkörningsläge. Den skriver inga filer, den jämför vad den skulle generera med vad som finns på disk och avslutar med kod 1 om något skiljer sig.
Här är ett minimalt GitHub Actions-arbetsflöde som använder det:
name: CI
on: [push, pull_request]
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
- run: npm ci
- run: npm run codegen:check
- run: npm run build
Steget codegen:check är bara meningsfullt om du committar genererade filer till repot. Om du inte gör det, ta bort det steget, eftersom npm run build regenererar allt via prebuild-hooken ändå.
Regenerera när schemat ändras
Operationer ändras i ditt repo, så watch-läget och prebuild fångar upp dem. Schemaändringar är svårare, eftersom de oftast kommer från ett annat team.
Hur du hanterar dem beror på var schemat finns:
- Schemafil i samma repo: Watch-läge och CI plockar upp ändringar, vilket är den enklaste setupen att arbeta med
- Schema från en URL: Codegen hämtar den senaste versionen vid varje körning, så
codegen:checki CI misslyckas så snart serverns schema inte längre matchar din committade utdata - Schema publicerat av ett annat repo: Kopiera eller ladda ner det som en del av din build och kör sedan Codegen på den lokala kopian
URL-alternativet har en hake. Watch-läget reagerar på filändringar, så det märker inte när ett fjärrschema ändras. Du ser de ändringarna först vid nästa manuella körning eller CI-jobb.
GraphQL Codegen vs handskrivna typer
Båda sätten ger dig TypeScript-typer för GraphQL-data, men det finns några skillnader du bör känna till.
Handskrivna typer
Med handskrivna typer läser du schemat och skriver matchande gränssnitt själv.
Detta ger dig full kontroll. Du kan namnge typer hur du vill, slå ihop ett par operationer till en delad typ eller lägga till hjälptyper som schemat inte har.
Men det är manuellt arbete. Varje nytt fält eller omdöpt argument betyder att du hittar och uppdaterar den matchande typen för hand. Och inget varnar dig när du missar en.
Det är där drift kommer ifrån. Gränssnittet Course från introduktionen kompilerade fortfarande efter att backend bytt namn på title, eftersom TypeScript bara kontrollerar din kod mot dina typer, inte mot API:et.
Genererade typer
Genererade typer kommer från samma schema som servern använder, så de är synkade med dina GraphQL-definitioner per design. En schemaändring plus en Codegen-körning uppdaterar alla berörda typer på en gång.
Du skriver också mycket mindre kod. Kurskatalogsprojektet har tre operationer och noll handskrivna typer för någon av dem.
Och du får schemadriven utveckling nästan gratis. Du ändrar schemat eller en operation, regenererar, och kompilatorn ger dig en lista över allt som måste ändras.
Men kom ihåg att Codegen bara speglar det du ger den.
Om schemat markerar varje fält som nullable, är dina genererade typer fulla av | null-kontroller. Om en operation hämtar 40 fält när sidan behöver fem, har den genererade typen 40 fält.
Här är en snabb sammanfattning av skillnaderna:
| Handskrivna typer | Genererade typer | |
|---|---|---|
| Underhåll | Manuellt vid varje schemaändring | Ett kommando efter varje ändring |
| Schemadrift | Möjlig och tyst | Fångas vid generering eller kompilering |
| Boilerplate | Växer med varje operation | Inget att skriva för hand |
| Kontroll över egna typer | Full | Begränsad till plugin-konfig |
| Schemadriven utveckling | Manuell korskontroll | Kompilatorn visar varje berörd rad |
Codegen jämfört med handskrivna typer
GraphQL Codegen vs. GraphQL-introspektion
De blandas ihop eftersom Codegen ofta använder introspektion. De löser ändå olika problem.
Introspektion är en funktion i GraphQL-specen. Varje GraphQL-server kan svara på queries om sitt eget schema via särskilda fält som __schema och __type. Resultatet är en JSON-beskrivning av varje typ, fält och argument som API:et exponerar.
På vanlig svenska svarar introspektion på frågan ”Hur ser det här API:et ut?”
Codegen svarar på frågan ”Vilken kod behöver min applikation?”. Den tar schemainformation och dina operationer, och skriver TypeScript-typer och typade dokumentnoder.
När du sätter schema till en URL i codegen.ts skickar Codegen en introspektionsfråga till den servern för att hämta schemat. Med en lokal .graphql-fil sker ingen introspektion alls.
Codegen kan också producera ett introspektionsresultat som utdata. Pluginet @graphql-codegen/introspection skriver schemat som introspektions-JSON, vilket vissa verktyg, som urql:s Graphcache, använder för schema-medveten caching.
| Introspektion | GraphQL Codegen | |
|---|---|---|
| Vad det är | Inbyggt GraphQL-frågesystem | Kommandoradsverktyg och plugin |
| Indata | En query till en körande server | Ett schema och dina operationer |
| Utdata | JSON-beskrivning av schemat | Typer, dokumentnoder, klientkod |
| När det körs | Närhelst en klient frågar servern | Under utveckling, builds och CI |
Codegen jämfört med introspektion
Vanliga problem med GraphQL Codegen
De flesta Codegen-fel kokar ner till ett av fyra problem.
Genererade typer saknas
Codegen körs utan fel, men typen du förväntar dig finns inte i utdatafilen.
Kontrollera dessa tre saker i ordning:
-
Schema: Om typen inte finns i schemat som Codegen laddar kan den inte genereras, så öppna schemafilen eller kör introspektionsfrågan själv och bekräfta att den finns
-
Documents: Operationstyper finns bara för operationer som Codegen hittade, så om filen med din query inte matchas av globmönstret
documentssaknas dess typer -
Plugins: Sedan v6 genererar
typescript-operationsbara enumar och inputs som dina operationer använder, så om du behöver en full schematyp somCourse, lägg till pluginettypescripti en separat utdatafil
Om du använder ett plugin du inte installerat misslyckas Codegen med ett tydligt meddelande:
Unable to find template plugin matching 'typescript-react-apollo'
Install one of the following packages:
- @graphql-codegen/typescript-react-apollo
Codegen kan inte hitta operationer
Det här felet betyder att globmönstret documents inte matchade någon fil med en operation i:
Unable to find any GraphQL type definitions for the following pointers:
- src/**/*.gql
Kontrollera att filändelsen i globen matchar dina filer. Ett .gql-mönster hittar inte .graphql-filer, och ett mönster src/**/*.graphql hittar inte operationer inline i .ts-filer.
För inline-operationer, kontrollera taggen också. Codegen hittar bara template literals inlindade i gql eller markerade med en /* GraphQL */-kommentar. En vanlig template-sträng med en query inuti är osynlig för den.
Om du sätter ignoreNoDocuments: true visar Codegen inte det här felet och genererar en utdata utan operationstyper. Det alternativet är användbart i ett helt nytt projekt, men kan dölja en trasig glob senare.
Genererade typer är inaktuella
Typerna kompilerar, men de speglar inte den senaste ändringen av en operation eller schemat.
Codegen körs inte av sig självt. Kör om det efter varje ändring i en .graphql-fil, en inline-operation, schemat eller codegen.ts. Under utveckling gör npm run codegen:watch det åt dig.
Om typerna är inaktuella i CI, committa de regenererade filerna eller låt prebuild-hooken regenerera dem före builden.
Genererade typer matchar inte API:et
Typerna kompilerar, men datan från servern har en annan form vid körning.
Det betyder nästan alltid att Codegen och din app pratar med olika scheman. Till exempel läser Codegen en lokal schema.graphql som är en vecka gammal, medan appen anropar ett produktions-API som ändrats sedan dess.
Kontrollera var nyckeln schema pekar. Om det är en fil, jämför den med API:et din app anropar och uppdatera den. Om det är en URL, se till att det är samma miljö som din app använder, inte en stagingserver med ett nyare eller äldre schema.
Regenerera sedan och titta på diffen. Om en operationsfil ändrades men den genererade filen inte gjorde det, ligger operationen troligen utanför globmönstret documents.
Best practices för GraphQL Codegen
Codegen behöver inte mycket konfiguration för att fungera bra. Dessa metoder håller det så när projektet växer:
-
Håll schema och genererade filer i synk: Kör watch-läge under utveckling och
prebuildföre builds, så att ingen jobbar med typer från ett äldre schema -
Generera operationsspecifika typer: Använd
typescript-operationsför klientkod eftersom de matchar exakt vad varje query hämtar -
Commita genererade filer bara när ditt arbetsflöde behöver det: Committade filer låter dig granska typändringar i pull requests och köra
--checki CI, och om ditt team inte behöver det, lägg till utdatakatalogen i.gitignoreoch generera vid varje build -
Håll konfigurationen kort: En utdatafil med två plugins täcker de flesta klientprojekt, och varje extra alternativ är något nästa utvecklare måste förstå innan något ändras
-
Kör Codegen i CI vid varje push: Ett
--check-steg eller enprebuild-hook fångar ändringarna någon glömde regenerera lokalt -
Redigera aldrig genererade filer för hand: Nästa Codegen-körning skriver över dina ändringar, så fixa schemat, operationen eller konfigurationen i stället
Slutsats
GraphQL Codegen tar över den mest repetitiva delen av GraphQL-utveckling – att för hand göra om scheman och operationer till TypeScript-typer.
Arbetsflödet är detsamma oavsett hur stort projektet blir. Du definierar schemat och operationerna, konfigurerar Codegen, genererar typer och typade dokumentnoder och importerar dem i din applikationskod.
Det största värdet är att dina typer alltid beskriver API:et du faktiskt anropar, inte koden du slipper skriva. När schemat ändras talar kompilatorn om ifall något bröts så att du kan fånga fel tidigt.
Om du vill lära dig hur GraphQL används i en modern NoSQL-databas, läs vårt blogginlägg MongoDB och GraphQL.
GraphQL Codegen vanliga frågor
Vad används GraphQL Codegen till?
GraphQL Codegen genererar kod från ett GraphQL-schema och dina GraphQL-operationer. De flesta team använder det för att få TypeScript-typer och typade dokumentnoder för sina queries och mutationer, så att de slipper skriva och uppdatera dessa typer för hand. Det kan också generera resolver-typer för servrar och färdiga hooks för specifika klienter.
Behöver jag en körande GraphQL-server för att använda Codegen?
Nej. Codegen kan läsa schemat från en lokal .graphql-fil, så det fungerar utan någon server. Du behöver bara en körande server om du pekar nyckeln schema mot en URL, eftersom Codegen då hämtar schemat via introspektion.
Ska jag committa genererade filer till Git?
Det beror på ert arbetsflöde. Committade filer låter dig granska typändringar i pull requests och använda graphql-codegen --check i CI för att fånga inaktuell utdata. Om ni inte behöver det, lägg till utdatakatalogen i .gitignore och regenerera filerna före varje build.
Ska jag använda client-presettet eller enskilda plugins?
The Guild rekommenderar client-presettet för de flesta klientappar, eftersom det paketerar operationstyper, typade dokumentnoder och fragmentmaskering i en setup. Apollo rekommenderar i stället pluginet typescript-operations, eftersom presetet lägger till körningskod och funktioner som krockar med Apollo Client. Om du använder Apollo, välj plugins. Annars är presetet ett bra standardval.
Varför får jag fel om dubbla deklarationer efter att jag uppdaterat Codegen?
Sedan v6 genererar pluginet typescript-operations sina egna enum- och inputtyper. Om din konfig fortfarande listar både typescript och typescript-operations för samma utdatafil deklarerar båda plugins typer som Level, och TypeScript kastar fel om dubbla deklarationer. Ta bort pluginet typescript från den utdatan, eller flytta det till en separat fil om du fortfarande behöver de fullständiga schematyperna.