Kursus
Tipe TypeScript yang ditulis manual untuk API GraphQL menjadi usang seketika saat seseorang mengubah skema.
Misalkan seorang developer backend mengganti nama field title pada tipe Course menjadi name. Frontend masih dapat dikompilasi karena interface Course yang ditulis manual tidak tahu ada perubahan, sehingga bug baru muncul saat runtime. Aplikasi dunia nyata cenderung memiliki banyak query, mutation, dan fragment, sehingga Anda bisa melihat betapa cepatnya ini dapat berkembang menjadi masalah besar.
GraphQL Code Generator, atau singkatnya GraphQL Codegen, membaca skema dan operasi GraphQL Anda lalu mengubahnya menjadi tipe, helper query, integrasi klien, dan boilerplate lain yang jika tidak akan Anda tulis secara manual. Alat ini paling sering digunakan dengan TypeScript dan klien populer seperti Apollo dan urql. Satu perintah akan menyelaraskan kembali tipe Anda setiap kali skema berubah.
Dalam artikel ini, saya akan memandu Anda menyiapkan GraphQL Codegen di proyek TypeScript kecil, mengonfigurasinya, menggunakan tipe yang dihasilkan, dan menjalankannya secara otomatis sebagai bagian dari alur kerja Anda.
Bagaimana perbandingan GraphQL dengan REST? Baca Graph QL vs REST: Panduan Lengkap kami untuk mempelajari perbedaan dan keunggulannya.
Apa Itu GraphQL Codegen?
GraphQL Codegen adalah alat baris perintah yang mengubah definisi GraphQL menjadi kode aplikasi.
Ini adalah proyek open-source dari The Guild, dan Anda menginstalnya sebagai dependensi pengembangan di proyek Anda. Alat ini menghubungkan tiga hal:
- Skema GraphQL: Kontrak antara server dan kliennya, berisi setiap tipe, field, dan argumen yang diekspos API
- Operasi GraphQL: Query, mutation, subscription, dan fragment yang dikirim aplikasi Anda ke API
- Kode yang dihasilkan: Tipe dan helper yang ditulis Codegen berdasarkan skema dan operasi Anda
Skema memberi tahu Codegen apa yang mungkin, dan operasi memberi tahu apa yang diminta aplikasi Anda.
Codegen menggabungkan keduanya, sehingga setiap tipe yang dihasilkan cocok dengan bentuk respons yang tepat untuk satu operasi. Alat ini juga memeriksa setiap operasi terhadap skema. Field title yang diganti namanya dari pengantar akan menghentikan proses pembuatan dengan galat seperti Cannot query field "title" on type "Course", dan Anda akan menangkapnya sebelum kode dijalankan.
Output bergantung pada plugin dan konfigurasi Anda. Codegen sendiri tidak menulis TypeScript. Plugin yang melakukannya. Misalnya, plugin typescript menghasilkan tipe skema, dan plugin typescript-operations menghasilkan tipe untuk query dan mutation Anda. Ada juga plugin untuk bahasa lain seperti Java dan C#, tetapi artikel ini berfokus pada TypeScript.
Satu hal yang perlu diingat di sini adalah bahwa Codegen tidak menggantikan server GraphQL Anda.
Alat ini tidak menyelesaikan query atau mengambil data. Ia membaca definisi GraphQL selama pengembangan dan menulis file. Server dan klien GraphQL Anda yang melakukan sisanya.
Cara Kerja GraphQL Codegen
Setiap setup Codegen mengikuti alur yang sama: skema + operasi GraphQL -> konfigurasi Codegen -> kode yang dihasilkan.

Diagram alur kerja GraphQL Codegen
Gambaran umumnya seperti ini:
-
Sediakan skema: Arahkan Codegen ke file
.graphqllokal atau endpoint GraphQL yang berjalan, yang akan dibaca Codegen melalui introspeksi -
Arahkan Codegen ke operasi Anda: Gunakan pola glob seperti
src/**/*.tsagar Codegen menemukan query, mutation, dan fragment di kode Anda -
Konfigurasikan plugin dan target output: Beri tahu Codegen file mana yang harus ditulis dan plugin mana yang menghasilkan tiap file
-
Jalankan Codegen: Jalankan
npx graphql-codegenuntuk memuat skema, memvalidasi operasi Anda, dan menulis output -
Gunakan artefak yang dihasilkan: Impor tipe dan document node yang dihasilkan ke dalam kode aplikasi Anda
Langkah 1 hingga 3 ditempatkan dalam satu file konfigurasi, biasanya codegen.ts. Berikut versi paling minimalnya untuk proyek katalog kursus:
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;
Preset client adalah bundel plugin yang menghasilkan tipe skema, tipe operasi, dan typed document node sekaligus. Saya akan membahas masing-masing dalam bagian setup dan konfigurasi.
Cara Menyiapkan GraphQL Codegen
Cara terbaik untuk memahami Codegen adalah menjalankannya pada proyek nyata.
Di bagian ini, Anda akan membangun proyek katalog kursus kecil dari nol. Proyek ini memiliki skema dengan kursus dan instruktur, satu query, dan konfigurasi Codegen yang mengubah query menjadi tipe TypeScript. Yang Anda butuhkan hanya Node.js dan npm.
Langkah 1: Instal GraphQL Code Generator
Buat folder baru dan inisialisasi proyek npm:
mkdir course-catalog && cd course-catalog
npm init -y
Codegen memerlukan beberapa paket. Dua masuk ke dependensi biasa, dan sisanya adalah dependensi pengembangan karena hanya dijalankan selama pengembangan:
npm install graphql @graphql-typed-document-node/core
npm install -D typescript @graphql-codegen/cli @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node
Berikut fungsi setiap paket:
-
graphql: Implementasi referensi GraphQL untuk JavaScript, yang digunakan Codegen untuk mengurai skema dan operasi -
@graphql-typed-document-node/core: Menyediakan tipeTypedDocumentNodeyang diimpor oleh kode hasil generate Anda -
@graphql-codegen/cli: Alat baris perintah Codegen -
@graphql-codegen/typescript-operations: Plugin yang menghasilkan tipe untuk query, mutation, dan fragment Anda -
@graphql-codegen/typed-document-node: Plugin yang menghasilkan objek dokumen dengan tipe terlampir
Langkah 2: Buat file konfigurasi
Codegen membaca pengaturannya dari file konfigurasi di root proyek. Buat codegen.ts dengan konfigurasi kosong untuk saat ini:
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {};
export default config;
Anda juga dapat menjalankan npx graphql-codegen init untuk menjawab beberapa pertanyaan dan mendapatkan file konfigurasi yang dihasilkan. Di sini saya menulisnya secara manual agar Anda dapat melihat fungsi setiap pengaturan.
Langkah 3: Tentukan lokasi skema dan dokumen
Codegen membutuhkan dua input, jadi mari kita buat keduanya.
Pertama, simpan skema sebagai schema.graphql di root proyek:
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!
}
Lalu, buat src/operations/courses.graphql dengan query yang mengambil kursus dan instrukturnya:
query GetCourses($level: Level, $limit: Int) {
courses(level: $level, limit: $limit) {
id
title
level
instructor {
name
}
}
}
Sekarang arahkan Codegen ke kedua file. Kunci schema menerima path skema, dan documents menerima pola glob yang cocok dengan operasi Anda:
const config: CodegenConfig = {
schema: "schema.graphql",
documents: ["src/**/*.graphql"],
};
Langkah 4: Pilih file output
Kunci generates memberi tahu Codegen di mana menulis output. Setiap kunci di dalamnya adalah path file:
const config: CodegenConfig = {
schema: "schema.graphql",
documents: ["src/**/*.graphql"],
generates: {
"src/generated/graphql.ts": {},
},
};
Codegen akan membuat folder src/generated/ jika belum ada.
Langkah 5: Tambahkan plugin yang relevan
File output tanpa plugin akan kosong. Tambahkan dua plugin yang Anda instal:
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;
Jika Anda mengikuti tutorial yang lebih lama, Anda mungkin juga melihat plugin typescript dalam daftar ini. Anda dapat menghapusnya sekarang, karena typescript-operations bisa bekerja sendiri dan hanya menghasilkan tipe Input, Enum, dan Operasi yang benar-benar digunakan. Jika Anda menempatkan keduanya dalam file output yang sama pada versi saat ini, TypeScript akan melempar galat deklarasi ganda.
Langkah 6: Jalankan Codegen
Tambahkan skrip npm agar Anda tidak perlu mengingat perintahnya:
npm pkg set scripts.codegen="graphql-codegen"
Lalu jalankan:
npm run codegen
Codegen memuat skema, memvalidasi query terhadapnya, dan menulis output:

Hasil proses generate Codegen
Jika sebuah operasi mereferensikan field yang tidak ada di skema, inilah saat proses berjalan gagal, dan tidak ada file yang ditulis.
Langkah 7: Periksa output yang dihasilkan
Buka src/generated/graphql.ts. Berikut yang akan Anda lihat:

Output yang dihasilkan
Codegen menghasilkan empat hal:
-
Level: Union string untuk enum, hanya dihasilkan karena query menggunakannya -
GetCoursesQueryVariables: Tipe untuk variabel yang Anda berikan ke query, keduanya opsional karena skema tidak menandainya sebagai wajib -
GetCoursesQuery: Bentuk respons, hanya dengan field yang diminta query -
GetCoursesDocument: Query yang telah diurai sebagai typed document node, dengan tipe hasil dan variabel terlampir
Lihat lagi GetCoursesQuery. Tipe ini tidak menyertakan durationHours atau bio milik instruktur, karena query tidak memintanya. Jika Anda mencoba membaca course.durationHours di kode, Anda akan mendapat galat kompilasi, bukan undefined saat runtime.
Dan itulah keseluruhan setup—Anda menulis GraphQL, dan Codegen menulis TypeScript.
Cara Mengonfigurasi GraphQL Codegen
Sebagian besar konfigurasi Codegen yang Anda lihat lebih panjang dari contoh di atas, namun dibangun dari empat pengaturan yang sama.
File konfigurasi bisa berupa codegen.ts, codegen.yml, atau codegen.json. Saya menggunakan TypeScript karena Anda mendapatkan autocomplete dan pemeriksaan tipe pada konfigurasi itu sendiri. Pengaturannya sama di setiap format.
Schema
Kunci schema memberi tahu Codegen di mana skema Anda ditempatkan. Ia menerima beberapa jenis sumber.
File lokal atau pola glob cocok saat skema ada di repo Anda:
schema: "schema.graphql",
// or, for a schema split across files
schema: "schema/**/*.graphql",
URL cocok saat skema ada di server yang berjalan. Codegen mengirimkan query introspeksi ke endpoint tersebut, dan Anda dapat meneruskan header untuk autentikasi:
schema: [
{
"https://api.example.com/graphql": {
headers: { Authorization: Bearer ${process.env.API_TOKEN} },
},
},
],
Opsi URL hanya berfungsi jika server mengaktifkan introspeksi. Banyak API produksi mematikannya, jadi untuk CI, file skema yang dikomit ke repo biasanya pilihan yang lebih aman.
Documents
Kunci documents memberi tahu Codegen di mana menemukan query, mutation, subscription, dan fragment Anda.
Operasi dapat ditempatkan di file .graphql atau inline di kode TypeScript Anda. Codegen menemukan operasi inline saat dibungkus dalam tag gql atau ditandai dengan komentar /* GraphQL */:
const EnrollStudent = /* GraphQL */
mutation EnrollStudent($courseId: ID!, $studentEmail: String!) {
enrollStudent(courseId: $courseId, studentEmail: $studentEmail) {
id
course {
title
}
}
}
;
Untuk memindai kedua jenis file, perluas pola glob. Tambahkan pola negasi agar Codegen tidak memindai outputnya sendiri:
documents: ["src/**/*.{graphql,ts}", "!src/generated/**"],
Beri setiap operasi nama unik. Codegen membangun nama tipe dari nama operasi, jadi EnrollStudent menjadi EnrollStudentMutation dan EnrollStudentMutationVariables.
Generates
Kunci generates memetakan path output ke isi yang dimasukkan ke dalamnya. Anda dapat memiliki sebanyak mungkin target output yang diperlukan, masing-masing dengan pluginnya sendiri.
Misalkan Anda menginginkan tipe operasi di satu file dan tipe skema lengkap di file lain:
generates: {
"src/generated/graphql.ts": {
plugins: ["typescript-operations", "typed-document-node"],
config: {
useTypeImports: true,
},
},
"src/generated/schema-types.ts": {
plugins: ["typescript"],
},
},
Objek config di dalam target output hanya berlaku untuk file tersebut. Di sini, useTypeImports membuat file yang dihasilkan menggunakan import type, yang dibutuhkan beberapa setup build. Jika Anda menempatkan config di root file, pengaturan tersebut berlaku untuk setiap output.
Path output juga bisa berupa folder yang diakhiri dengan garis miring, seperti "src/gql/". Itulah format yang digunakan preset, karena mereka menulis lebih dari satu file.
Plugins
Plugin menentukan apa yang dihasilkan. Codegen memuat skema dan dokumen, lalu setiap plugin mengubahnya menjadi bagian output.
Berikut plugin yang paling sering Anda lihat di proyek TypeScript:
-
typescript-operations: Tipe untuk query, mutation, subscription, dan fragment Anda, plus enum dan input yang digunakan -
typed-document-node: Objek dokumen dengan tipe hasil dan variabel terlampir, yang dipahami sebagian besar klien GraphQL -
typescript: Tipe untuk setiap objek, enum, dan input dalam skema, terlepas dari apakah operasi Anda menggunakannya atau tidak -
typescript-resolvers: Tanda tangan tipe untuk fungsi resolver di sisi server
Sebuah preset adalah bundel plugin dengan konfigurasi default yang masuk akal. Preset client dari bagian Cara Kerjanya adalah yang direkomendasikan The Guild untuk aplikasi klien. Preset ini dibangun di atas typescript-operations yang telah ditingkatkan, sehingga Anda mendapat manfaatnya tanpa setup tambahan.
Setiap plugin memiliki opsi konfigurasinya sendiri, dan jumlahnya banyak. Anda tidak perlu mempelajarinya di awal. Mulailah dengan default dan cari opsi saat output yang dihasilkan tidak cocok dengan yang diharapkan kode Anda.
Apa yang Dapat Dihasilkan GraphQL Codegen?
Apa yang Anda dapatkan dari Codegen tergantung pada plugin yang Anda pilih.
Skema dan operasi yang sama dapat menghasilkan tipe TypeScript biasa atau kode klien siap pakai. Berikut output yang paling sering Anda gunakan.
Tipe TypeScript
Plugin typescript menghasilkan tipe untuk setiap objek, enum, dan input di skema Anda. Untuk katalog kursus, tipe Course terlihat seperti ini:
export type Course = {
__typename?: 'Course';
durationHours: Scalars['Int']['output'];
id: Scalars['ID']['output'];
instructor: Instructor;
level: Level;
title: Scalars['String']['output'];
};
Tipe-tipe ini menggambarkan skema lengkap, sehingga berguna di sisi server, di fixture pengujian, dan di fungsi utilitas yang bekerja dengan objek lengkap.
Namun tipe ini kurang cocok untuk kode klien. Tipe Course menyatakan setiap field ada, bahkan yang tidak pernah diminta oleh query Anda.
Tipe operasi
Plugin typescript-operations menghasilkan dua tipe per operasi: satu untuk hasil dan satu untuk variabel. Cara kerjanya sama untuk query dan subscription.
Berikut yang dihasilkannya untuk mutation EnrollStudent:
export type EnrollStudentMutationVariables = Exact<{
courseId: string | number;
studentEmail: string;
}>;
export type EnrollStudentMutation = { enrollStudent: { id: string, course: { title: string } } };
Tipe hasil cocok dengan selection set, field demi field. Tipe variabel adalah Exact, sehingga TypeScript menolak variabel tambahan atau salah eja.
Variabel courseId menerima string dan number karena skalar ID GraphQL menerima keduanya sebagai input. Sejak v6, tipe default adalah string | number, yang merupakan tipe yang benar untuk kasus penggunaan klien.
Typed document node
Sebuah DocumentNode biasa adalah query GraphQL yang telah diurai. Ia mengetahui struktur query, tetapi tidak mengetahui apa yang dikembalikan query atau variabel apa yang diterimanya.
Typed document node memiliki keduanya. Plugin typed-document-node menghasilkan satu per operasi:
export const GetCoursesDocument = { /* ... */ } as unknown as DocumentNode<GetCoursesQuery, GetCoursesQueryVariables>;
Dengan cara ini, fungsi apa pun yang menerima TypedDocumentNode dapat menyimpulkan tipe hasil dan variabel hanya dari dokumennya. Anda tidak perlu meneruskan generic secara manual, dan Anda tidak bisa mengombinasikan query dengan tipe yang salah.
Sebagian besar klien GraphQL modern menerima typed document node, termasuk Apollo Client, urql, dan graphql-request. Anda akan melihat ketiganya beraksi nanti di artikel ini.
Integrasi spesifik klien
Beberapa plugin melangkah lebih jauh dan menghasilkan kode untuk klien atau framework tertentu. Misalnya:
-
typescript-react-apollo: Menghasilkan hook React yang bertipe untuk Apollo Client -
typescript-urql: Menghasilkan hook dan komponen bertipe untuk urql -
typescript-react-query: Menghasilkan hook bertipe untuk TanStack Query -
typescript-graphql-request: Menghasilkan SDK bertipe dengan satu fungsi per operasi
Semua ini adalah plugin komunitas. The Guild memindahkannya ke repositori komunitas terpisah, sehingga kecepatan rilisnya berbeda dari plugin inti.
Anda juga membutuhkannya lebih jarang daripada sebelumnya. Ketika klien menerima typed document node, hook yang dihasilkan tidak banyak menambah keamanan tipe. Dokumentasi Apollo, misalnya, merekomendasikan menggunakan plugin typescript-operations, yang berfokus hanya pada pembuatan tipe dan tidak menyertakan kode runtime tambahan.
Secara umum, Anda harus mulai dengan tipe operasi dan typed document node. Lalu, tambahkan plugin spesifik klien hanya jika tim Anda menginginkan hook atau fungsi SDK yang dihasilkan.
GraphQL Codegen dengan TypeScript
Tipe yang dihasilkan hanya berarti jika mereka menangkap kesalahan, jadi mari kita buat beberapa.
Contoh ini memperluas proyek katalog kursus dengan query kedua, helper kecil yang mengirimkannya ke API, dan kode yang menggunakan hasilnya. Diasumsikan sebuah server GraphQL yang mengimplementasikan skema berjalan di http://localhost:4000/graphql.
Berikut bagian skema yang digunakan contoh ini:
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
}
Dua detail penting di sini. Field course mengembalikan Course tanpa !, jadi nilainya null ketika tidak ada kursus yang cocok dengan ID. bio milik instruktur juga bisa null.
Buat src/operations/course.graphql dengan query yang mengambil satu kursus:
query GetCourse($id: ID!) {
course(id: $id) {
title
durationHours
instructor {
name
bio
}
}
}
Jalankan npm run codegen lagi. Codegen menambahkan dua tipe baru ke src/generated/graphql.ts:

Isi file graphql.ts
Keter-null-an dari skema ikut terbawa. course bisa null, bio bisa null, dan id wajib karena query mendeklarasikannya sebagai ID!.
Sekarang untuk kode aplikasi. Buat src/execute.ts dengan helper yang mengirim dokumen bertipe apa pun ke API:
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;
}
Helper ini tidak tahu apa-apa tentang kursus. Ia mendapatkan kedua tipe dari dokumen yang Anda berikan, dan fungsi print() mengubah dokumen kembali menjadi string query untuk permintaan.
Lalu gunakan di 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");
}
Fungsi formatCourse() mengambil tipe parameternya dari tipe query yang dihasilkan. NonNullable menghapus kasus null, karena fungsi hanya dijalankan setelah pengecekan if.
Contoh menggunakan await pada level atas, jadi setel "type": "module" di package.json dan jalankan dengan npx tsx src/course-details.ts:

Panggilan berhasil ke server GraphQL mock
Sekarang, ini dengan asumsi Anda memiliki server GraphQL yang berjalan. Berikut server mock yang saya gunakan:
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");
});
Sekarang coba rusak-kan. Buka src/course-details.ts dan ganti semua yang ada di bawah fungsi formatCourse() dengan tiga kesalahan yang sering lolos saat tipe ditulis manual:
// 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);
Lalu lakukan pemeriksaan tipe pada proyek:
npx tsc -p .
TypeScript menangkap ketiganya sebelum kode dijalankan:

Galat yang ditangkap oleh TypeScript
Begitulah cara Codegen mengurangi ketidaksesuaian antara frontend dan API. Tipe-tipe berasal dari skema yang sama dengan yang digunakan server, sehingga tidak mungkin mereka menggambarkan field yang tidak ada atau melewatkan nilai null yang dapat dikembalikan server.
Dan perlindungan tidak berhenti setelah run pertama. Misalkan tim backend membuat durationHours bisa null. Anda menghasilkan ulang, tipe berubah menjadi number | null, dan TypeScript menunjukkan setiap baris yang mengasumsikan number.
GraphQL Codegen dengan Klien GraphQL
Codegen tidak mengirim permintaan. Itu tugas klien GraphQL Anda.
Typed document node yang dihasilkan adalah titik serah antara keduanya. Anda meneruskannya ke klien, dan klien menyimpulkan tipe. Contoh di bawah menggunakan file src/generated/graphql.ts yang sama dan API mock dari bagian sebelumnya, jadi jalankan npx tsx mock/server.ts di terminal terpisah.
GraphQL Codegen dengan Apollo
Instal Apollo Client dan dependensi sejajarnya rxjs:
npm install @apollo/client rxjs
Contoh ini juga menjalankan mutation EnrollStudent. Simpan sebagai src/operations/enroll.graphql:
mutation EnrollStudent($courseId: ID!, $studentEmail: String!) {
enrollStudent(courseId: $courseId, studentEmail: $studentEmail) {
id
course {
title
}
}
}
Dokumentasi Apollo juga merekomendasikan dua opsi konfigurasi untuk Codegen, karena Apollo Client selalu menyertakan field __typename tetapi tidak menambahkannya ke tipe root. Tambahkan ke target output di codegen.ts:
"src/generated/graphql.ts": {
plugins: ["typescript-operations", "typed-document-node"],
config: {
nonOptionalTypename: true,
skipTypeNameForRoot: true,
},
},
Dengan opsi ini, setiap objek dalam tipe hasil yang dihasilkan mendapatkan field __typename yang wajib, seperti __typename: 'Course'. Itu cocok dengan apa yang dikembalikan cache Apollo.
Jalankan npm run codegen lagi untuk mendeteksi mutation dan konfigurasi baru.
Apollo Client menerima typed document node di query() dan mutate(). Simpan ini sebagai 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);
Objek data diberi tipe GetCoursesQuery | undefined, dan TypeScript memeriksa variables terhadap GetCoursesQueryVariables. Mutation bekerja dengan cara yang sama dengan EnrollStudentMutation dan variabelnya.
Jalankan dengan npx tsx src/apollo.ts:

Output Apollo
Baris pertama berasal dari query, karena hanya itu kursus pemula. Baris kedua berasal dari mutation, yang mengembalikan judul kursus tempat mahasiswa mendaftar.
Hook React, seperti useQuery() dan useMutation(), menyimpulkan tipe dari typed document node dengan cara yang sama.
GraphQL Codegen dengan urql
Instal paket inti urql:
npm install @urql/core
urql bekerja dengan typed document node secara langsung. Teruskan dokumen sebagai argumen pertama ke query() di 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));
Argumen kedua diberi tipe sebagai variabel query, dan result.data diberi tipe sebagai hasil query.
Jalankan dengan npx tsx src/urql.ts:

Output Urql
Binding framework, seperti useQuery() di paket urql untuk React, juga menerima typed document node.
GraphQL Codegen dengan klien lain
Anda tidak membutuhkan klien berfitur lengkap untuk mendapatkan hasil bertipe.
Klien graphql-request adalah klien minimal yang mengirim satu permintaan dan mengembalikan data. Instal dengan perintah berikut:
npm install graphql-request
Klien ini menyimpulkan tipe dari typed document node seperti Apollo dan urql. Simpan ini sebagai 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);
Jalankan dengan npx tsx src/graphql-request.ts:

Output permintaan GraphQL
Gagasan yang sama berlaku untuk TanStack Query. Paket ini tidak mengirim permintaan GraphQL sendiri, jadi Anda memanggil graphql-request atau helper execute() dari bagian sebelumnya di dalam fungsi query, dan tipe hasil akan mengalir ke kode Anda.
Dan jika klien Anda sama sekali tidak mendukung typed document node, Anda tetap dapat menggunakan tipe operasi biasa. Impor GetCourseQuery dan GetCourseQueryVariables dan berikan sebagai generic. Itu memang lebih manual, tetapi tipenya tetap berasal dari skema.
Cara Menjalankan GraphQL Codegen Secara Otomatis
Tipe yang dihasilkan hanya membantu jika mereka cocok dengan skema dan operasi saat ini.
Jika Anda lupa menjalankan ulang Codegen setelah perubahan, tipe menggambarkan kode yang tidak lagi ada. Itu masalah drift yang sama seperti tipe yang ditulis manual, hanya dengan langkah ekstra. Solusinya adalah menghilangkan langkah manual dari alur kerja.
Skrip npm
Mulailah dengan beberapa skrip di package.json, sehingga semua orang di tim menjalankan Codegen dengan cara yang sama:
{
"scripts": {
"codegen": "graphql-codegen",
"codegen:watch": "graphql-codegen --watch",
"codegen:check": "graphql-codegen --check",
"prebuild": "npm run codegen",
"build": "tsc -p ."
}
}
Sisa bagian ini membahas fungsi masing-masing.
Mode watch
Mode watch menjalankan ulang Codegen setiap kali file yang dipantau berubah. Mode ini membutuhkan paket @parcel/watcher, yang tidak diinstal Codegen untuk Anda:
npm install -D @parcel/watcher
npm run codegen:watch
Jika Anda melewatkan instalasi, Codegen berjalan sekali dan menampilkan Failed to import @parcel/watcher alih-alih memantau.
Mode watch melacak operasi Anda dan file skema lokal. Jika Anda menambahkan field ke course.graphql, field baru akan muncul di GetCourseQuery satu detik kemudian. Jika Anda membuat durationHours bisa null di schema.graphql, tipe berubah menjadi number | null, dan editor Anda menandai setiap baris yang mengasumsikan number.
Biarkan mode ini berjalan di terminal di samping server dev Anda selama pengembangan.
Generate sebelum build
npm menjalankan skrip bernama prebuild sebelum build. Dengan skrip di atas, npm run build menghasilkan ulang tipe terlebih dahulu dan baru kemudian menjalankan kompilator TypeScript.
Dengan cara ini, build tidak pernah berjalan dengan tipe basi, bahkan jika seseorang lupa menjalankan Codegen setelah menarik perubahan.
CI/CD
Flag --check menjalankan Codegen dalam mode dry-run. Ia tidak menulis file apa pun, membandingkan apa yang akan dihasilkan dengan yang ada di disk dan keluar dengan kode 1 jika ada yang berbeda.
Berikut workflow GitHub Actions minimal yang menggunakannya:
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
Langkah codegen:check hanya masuk akal jika Anda mengomit file yang dihasilkan ke repo. Jika tidak, hapus langkah tersebut, karena npm run build akan menghasilkan ulang semuanya melalui hook prebuild.
Hasilkan ulang saat skema berubah
Operasi berubah di repo Anda, jadi mode watch dan prebuild mengovernya. Perubahan skema lebih sulit, karena biasanya berasal dari tim lain.
Cara menanganinya tergantung di mana skema ditempatkan:
- File skema di repo yang sama: Mode watch dan CI menangkap perubahan, ini setup yang paling mudah digunakan
- Skema dari URL: Codegen mengambil versi terbaru pada setiap run, jadi
codegen:checkdi CI gagal segera setelah skema server tidak lagi cocok dengan output yang Anda komit - Skema yang dipublikasikan oleh repo lain: Salin atau unduh sebagai bagian dari build Anda, lalu jalankan Codegen pada salinan lokal
Opsi URL memiliki satu kelemahan. Mode watch bereaksi pada perubahan file, jadi tidak menyadari saat skema jarak jauh berubah. Anda hanya akan melihat perubahan itu pada run manual berikutnya atau job CI.
GraphQL Codegen vs Tipe yang Ditulis Manual
Kedua pendekatan memberi Anda tipe TypeScript untuk data GraphQL, tetapi ada beberapa perbedaan yang perlu Anda ketahui.
Tipe yang ditulis manual
Dengan tipe yang ditulis manual, Anda membaca skema dan menulis interface yang cocok sendiri.
Ini memberi Anda kontrol penuh. Anda bisa menamai tipe sesuka Anda, menggabungkan beberapa operasi menjadi satu tipe bersama, atau menambahkan tipe helper yang tidak dimiliki skema.
Namun itu pekerjaan manual. Setiap field baru atau argumen yang diganti nama berarti Anda harus mencari dan memperbarui tipe yang cocok secara manual. Dan tidak ada yang memperingatkan saat Anda melewatkannya.
Di situlah drift berasal. Interface Course dari pengantar masih dapat dikompilasi setelah backend mengganti title, karena TypeScript hanya memeriksa kode Anda terhadap tipe Anda, bukan terhadap API.
Tipe yang dihasilkan
Tipe yang dihasilkan berasal dari skema yang sama dengan yang digunakan server, sehingga secara desain disinkronkan dengan definisi GraphQL Anda. Perubahan skema ditambah run Codegen memperbarui setiap tipe yang terpengaruh sekaligus.
Anda juga menulis jauh lebih sedikit kode. Proyek katalog kursus memiliki tiga operasi dan nol tipe yang ditulis manual untuk semuanya.
Dan Anda hampir mendapatkan pengembangan berbasis skema secara gratis. Anda mengubah skema atau operasi, menghasilkan ulang, dan kompilator memberi Anda daftar semua yang perlu diubah.
Namun perlu diingat bahwa Codegen hanya mencerminkan apa yang Anda berikan.
Jika skema menandai setiap field sebagai bisa null, tipe yang dihasilkan penuh dengan pengecekan | null. Jika sebuah operasi mengambil 40 field saat halaman hanya perlu lima, tipe yang dihasilkan memiliki 40 field.
Berikut ringkasan cepat perbedaannya:
| Tipe yang ditulis manual | Tipe yang dihasilkan | |
|---|---|---|
| Perawatan | Manual untuk setiap perubahan skema | Satu perintah setelah setiap perubahan |
| Schema drift | Mungkin terjadi dan tanpa peringatan | Tertangkap saat generate atau waktu kompilasi |
| Boilerplate | Bertambah dengan setiap operasi | Tidak ada yang ditulis manual |
| Kontrol atas tipe kustom | Penuh | Terbatas pada opsi konfigurasi plugin |
| Pengembangan berbasis skema | Pencocokan manual | Kompilator menunjukkan setiap baris yang terpengaruh |
Perbandingan Codegen dengan tipe yang ditulis manual
GraphQL Codegen vs. Introspeksi GraphQL
Keduanya sering tertukar karena Codegen sering menggunakan introspeksi. Namun keduanya menyelesaikan masalah yang berbeda.
Introspeksi adalah fitur spesifikasi GraphQL. Server GraphQL mana pun dapat menjawab query tentang skemanya sendiri melalui field khusus seperti __schema dan __type. Hasilnya adalah deskripsi JSON dari setiap tipe, field, dan argumen yang diekspos API.
Secara sederhana, introspeksi menjawab pertanyaan "Seperti apa API ini?"
Codegen menjawab pertanyaan "Kode apa yang dibutuhkan aplikasi saya?". Ia mengambil informasi skema dan operasi Anda, lalu menulis tipe TypeScript dan typed document node.
Saat Anda mengatur schema ke URL di codegen.ts, Codegen mengirim query introspeksi ke server tersebut untuk mendapatkan skema. Dengan file .graphql lokal, introspeksi sama sekali tidak terjadi.
Codegen juga dapat menghasilkan hasil introspeksi sebagai output. Plugin @graphql-codegen/introspection menulis skema sebagai JSON introspeksi, yang digunakan beberapa alat, seperti Graphcache milik urql, untuk caching yang paham skema.
| Introspeksi | GraphQL Codegen | |
|---|---|---|
| Apa itu | Sistem query bawaan GraphQL | Alat baris perintah dan plugin |
| Input | Sebuah query ke server yang berjalan | Sebuah skema dan operasi Anda |
| Output | Deskripsi JSON dari skema | Tipe, document node, kode klien |
| Kapan dijalankan | Kapan pun klien meminta ke server | Selama pengembangan, build, dan CI |
Perbandingan Codegen dengan introspeksi
Masalah Umum GraphQL Codegen
Sebagian besar galat Codegen bermuara pada salah satu dari empat masalah.
Tipe yang dihasilkan hilang
Codegen berjalan tanpa galat, tetapi tipe yang Anda harapkan tidak ada di file output.
Periksa tiga hal ini secara berurutan:
-
Skema: Jika tipe tidak ada di skema yang dimuat Codegen, maka tipe tersebut tidak dapat dihasilkan, jadi buka file skema atau jalankan query introspeksi sendiri dan pastikan tipe itu ada
-
Dokumen: Tipe operasi hanya ada untuk operasi yang ditemukan Codegen, jadi jika file dengan query Anda tidak cocok dengan glob
documents, tipenya akan hilang -
Plugin: Sejak v6,
typescript-operationshanya menghasilkan enum dan input yang digunakan operasi Anda, jadi jika Anda membutuhkan tipe skema penuh sepertiCourse, tambahkan plugintypescriptke file output terpisah
Jika Anda menggunakan plugin yang belum diinstal, Codegen gagal dengan pesan yang jelas:
Unable to find template plugin matching 'typescript-react-apollo'
Install one of the following packages:
- @graphql-codegen/typescript-react-apollo
Codegen tidak dapat menemukan operasi
Galat ini berarti glob documents tidak cocok dengan file mana pun yang berisi operasi di dalamnya:
Unable to find any GraphQL type definitions for the following pointers:
- src/**/*.gql
Periksa apakah ekstensi di glob cocok dengan file Anda. Pola .gql tidak akan menemukan file .graphql, dan pola src/**/*.graphql tidak akan menemukan operasi yang inline di file .ts.
Untuk operasi inline, periksa juga tag-nya. Codegen hanya menemukan template literal yang dibungkus gql atau ditandai dengan komentar /* GraphQL */. Template string biasa dengan query di dalamnya tidak terlihat oleh Codegen.
Jika Anda menyetel ignoreNoDocuments: true, Codegen tidak menampilkan galat ini dan menghasilkan output tanpa tipe operasi. Opsi tersebut berguna di proyek yang benar-benar baru, tetapi dapat menyembunyikan glob yang rusak di kemudian hari.
Tipe yang dihasilkan sudah usang
Tipe dapat dikompilasi, tetapi tidak mencerminkan perubahan terbaru pada operasi atau skema.
Codegen tidak berjalan dengan sendirinya. Jalankan ulang setelah setiap perubahan pada file .graphql, operasi inline, skema, atau codegen.ts. Selama pengembangan, npm run codegen:watch melakukan itu untuk Anda.
Jika tipe basi di CI, komit file yang dihasilkan ulang atau biarkan hook prebuild menghasilkan ulang sebelum build.
Tipe yang dihasilkan tidak cocok dengan API
Tipe dapat dikompilasi, tetapi data dari server memiliki bentuk berbeda saat runtime.
Hampir selalu itu berarti Codegen dan aplikasi Anda berbicara pada skema yang berbeda. Misalnya, Codegen membaca schema.graphql lokal yang berumur seminggu, sementara aplikasi memanggil API produksi yang telah berubah sejak saat itu.
Periksa ke mana kunci schema mengarah. Jika itu file, bandingkan dengan API yang dipanggil aplikasi Anda, dan perbarui. Jika itu URL, pastikan itu lingkungan yang sama yang digunakan aplikasi Anda, bukan server staging dengan skema yang lebih baru atau lebih lama.
Lalu hasilkan ulang dan lihat perbedaannya. Jika file operasi berubah tetapi file yang dihasilkan tidak, kemungkinan operasi berada di luar glob documents.
Praktik Terbaik untuk GraphQL Codegen
Codegen tidak membutuhkan banyak konfigurasi agar bekerja dengan baik. Praktik berikut membuatnya tetap demikian seiring pertumbuhan proyek:
-
Jaga skema dan file yang dihasilkan tetap sinkron: Jalankan mode watch selama pengembangan dan
prebuildsebelum build, sehingga tidak ada yang bekerja dengan tipe dari skema lama -
Hasilkan tipe spesifik operasi: Gunakan
typescript-operationsuntuk kode klien karena tipe ini persis cocok dengan apa yang diambil setiap query -
Komit file yang dihasilkan hanya saat alur kerja Anda membutuhkannya: File yang dikomit memungkinkan Anda meninjau perubahan tipe di pull request dan menjalankan
--checkdi CI, dan jika tim Anda tidak memerlukannya, tambahkan folder output ke.gitignoredan hasilkan pada setiap build -
Jaga konfigurasi tetap singkat: Satu file output dengan dua plugin sudah mencakup sebagian besar proyek klien, dan setiap opsi tambahan adalah sesuatu yang harus dipahami developer berikutnya sebelum mengubah apa pun
-
Jalankan Codegen di CI pada setiap push: Langkah
--checkatau hookprebuildmenangkap perubahan yang seseorang lupa hasilkan ulang secara lokal -
Jangan pernah mengedit file yang dihasilkan secara manual: Run Codegen berikutnya akan menimpa perubahan Anda, jadi perbaiki skema, operasi, atau konfigurasi sebagai gantinya
Kesimpulan
GraphQL Codegen mengambil alih bagian paling berulang dari pengembangan GraphQL—mengubah skema dan operasi menjadi tipe TypeScript secara manual.
Alur kerjanya tetap sama tak peduli seberapa besar proyeknya. Anda mendefinisikan skema dan operasi, mengonfigurasi Codegen, menghasilkan tipe dan typed document node, lalu mengimpornya ke kode aplikasi Anda.
Nilai terbesarnya adalah tipe Anda selalu menggambarkan API yang benar-benar Anda panggil, bukan kode yang tidak perlu Anda tulis. Saat skema berubah, kompilator akan memberi tahu jika ada yang rusak sehingga Anda dapat menangkap galat lebih awal.
Jika Anda ingin mempelajari bagaimana GraphQL digunakan dalam database NoSQL modern, baca MongoDB dan GraphQL di blog kami.
GraphQL Codegen FAQs
Untuk apa GraphQL Codegen digunakan?
GraphQL Codegen menghasilkan kode dari skema GraphQL dan operasi GraphQL Anda. Sebagian besar tim menggunakannya untuk mendapatkan tipe TypeScript dan typed document node untuk query dan mutation mereka, sehingga mereka tidak perlu menulis dan memperbarui tipe tersebut secara manual. Alat ini juga dapat menghasilkan tipe resolver untuk server dan hook siap pakai untuk klien tertentu.
Apakah saya memerlukan server GraphQL yang berjalan untuk menggunakan Codegen?
Tidak. Codegen dapat membaca skema dari file .graphql lokal, sehingga berfungsi tanpa server apa pun. Anda hanya memerlukan server yang berjalan jika Anda mengarahkan kunci schema ke URL, karena Codegen kemudian mengambil skema melalui introspeksi.
Haruskah saya mengomit file yang dihasilkan ke Git?
Tergantung pada alur kerja tim Anda. File yang dikomit memungkinkan Anda meninjau perubahan tipe di pull request dan menggunakan graphql-codegen --check di CI untuk menangkap output basi. Jika Anda tidak memerlukannya, tambahkan folder output ke .gitignore dan hasilkan ulang file sebelum setiap build.
Haruskah saya menggunakan preset client atau plugin individual?
The Guild merekomendasikan preset client untuk sebagian besar aplikasi klien, karena membundel tipe operasi, typed document node, dan fragment masking dalam satu setup. Apollo merekomendasikan plugin typescript-operations sebagai gantinya, karena preset menambahkan kode runtime dan fitur yang bertentangan dengan Apollo Client. Jika Anda menggunakan Apollo, gunakan plugin. Selain itu, preset adalah default yang baik.
Mengapa saya mendapat galat deklarasi ganda setelah memperbarui Codegen?
Sejak v6, plugin typescript-operations menghasilkan tipe enum dan inputnya sendiri. Jika konfigurasi Anda masih mencantumkan typescript dan typescript-operations untuk file output yang sama, kedua plugin mendeklarasikan tipe seperti Level, dan TypeScript melempar galat deklarasi ganda. Hapus plugin typescript dari output tersebut, atau pindahkan ke file terpisah jika Anda masih memerlukan tipe skema lengkap.
