# Keamanan Tipe End-to-End dengan openapi-typescript

> Hapus interface API tulis tangan Anda dan hasilkan dari spesifikasi OpenAPI — bagaimana openapi-typescript dan openapi-fetch memberi Anda klien bertipe dan menangkap penyimpangan backend di compile time.

URL: https://www.matthewswong.com/id/blog/openapi-typescript-end-to-end-types/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-07-14
- Updated: 2026-07-14
- Category: Backend

> **Key takeaway:** Arahkan openapi-typescript ke spesifikasi OpenAPI Anda untuk menghasilkan file tipe, lalu berikan tipe itu ke createClient milik openapi-fetch untuk klien HTTP yang sepenuhnya bertipe. Setiap path, query param, request body, dan response diperiksa oleh compiler, jadi perubahan backend yang merusak kontrak menggagalkan build Anda alih-alih produksi.

Setiap tim TypeScript fullstack pada akhirnya menabrak tembok yang sama: backend mengganti nama sebuah field, merilisnya, dan frontend tetap mengirim bentuk lama karena tidak ada yang memperbarui interface yang ditulis tangan. Compiler hijau, tes lolos, dan bug baru muncul saat pengguna sungguhan memuat halaman. Saya sudah mengejar jenis kegagalan ini lebih sering daripada yang ingin saya akui, dan solusinya bukan disiplin yang lebih ketat. Solusinya adalah menghapus tipe yang ditulis tangan sepenuhnya dan menghasilkannya dari satu artefak yang sudah disepakati kedua sisi: spesifikasi OpenAPI.

Tulisan ini membahas pasangan yang selalu saya gunakan pada setiap frontend Next.js yang berbicara dengan backend NestJS: openapi-typescript untuk mengubah spesifikasi menjadi file tipe, dan openapi-fetch untuk membungkus tipe itu dalam klien kecil tanpa runtime. Hasilnya adalah keamanan tipe end-to-end tanpa generic yang harus ditulis dan tanpa framework codegen yang harus diurus.

## Mengapa tipe klien yang ditulis tangan selalu melenceng

Interface TypeScript yang dipelihara tangan untuk sebuah response API adalah salinan. Begitu Anda menulisnya, ia mulai menyimpang dari sumber kebenaran di server. Tidak ada mekanisme yang memaksa keduanya tetap sama, jadi penyimpangan bukan risiko, melainkan kondisi default seiring waktu. Compiler tidak bisa membantu di sini karena ia memvalidasi kode Anda terhadap salinan basi Anda sendiri, bukan terhadap kenyataan.

Tipe yang dihasilkan membalik hubungan ini. Alih-alih salinan yang Anda pelihara, Anda memiliki proyeksi dari spesifikasi yang Anda regenerasi. Ketika backend mengubah kontrak dan menerbitkan ulang spesifikasi, regenerasi menghasilkan file tipe yang berbeda, dan setiap lokasi pemanggilan yang tidak lagi cocok menjadi merah. Warna merah itulah intinya: ketidakcocokan berpindah dari runtime, tempat pengguna menemukannya, ke compile time, tempat Anda menemukannya.

| Aspek | Interface tulis tangan | Dihasilkan dari OpenAPI |
| --- | --- | --- |
| Sumber kebenaran | Salinan di repo frontend | Spesifikasi yang diterbitkan backend |
| Penyimpangan seiring waktu | Pasti, secara diam-diam | Tertangkap saat regenerasi |
| Perubahan yang merusak muncul | Saat runtime, di produksi | Saat compile time, di CI |
| Biaya pemeliharaan | Edit manual per perubahan | Satu perintah regenerasi |

## Langkah satu: hasilkan tipe dari spesifikasi

openapi-typescript adalah alat khusus build-time. Ia membaca dokumen OpenAPI 3.x, dari file lokal atau URL, dan menghasilkan satu file .d.ts. Ia tidak menghasilkan kode runtime apa pun, jadi tidak ada yang dihasilkannya masuk ke bundle Anda. Saat tulisan ini dibuat, versi terkini adalah 7.x, dan ia mengharapkan TypeScript 5.0 atau lebih baru untuk hasil terbaik.

File yang dihasilkan mengekspor interface paths yang mendeskripsikan setiap route, ditambah components untuk skema bersama. Anda tidak pernah menulisnya dengan tangan dan jarang membacanya langsung. Mereka ada untuk memberi makan klien di langkah berikutnya. Jika backend Anda NestJS, modul @nestjs/swagger dapat menyajikan persis dokumen JSON yang dikonsumsi perintah kedua di atas, yang menutup siklus tanpa langkah ekspor manual apa pun.

## Langkah dua: klien bertipe dengan openapi-fetch

openapi-fetch adalah bagian runtime, dan sengaja dibuat sangat kecil, sekitar enam kilobyte dengan overhead yang praktis nol di atas fetch native yang dibungkusnya. Anda memanggil createClient dengan tipe paths yang dihasilkan sebagai argumen tipe, dan sejak saat itu klien mengetahui setiap route yang diekspos API Anda. Tidak ada generic per-panggilan yang harus ditulis dan tidak ada pengetikan manual. String path itu sendiri menggerakkan inferensi untuk params, body, dan response.

Autocomplete argumen pertama dan Anda mendapatkan daftar route sungguhan. Salah menulis nama path param, lupa field query yang wajib, atau mengirim number saat spesifikasi meminta string, dan compiler menolaknya sebelum kode berjalan. Inilah imbalan inferensi di atas generic: ergonomi tetap identik dengan fetch biasa, tetapi jaminannya menyeluruh.

## Error dan response juga bertipe

Setiap panggilan mengembalikan objek dengan data, error, dan response. Properti data terisi hanya pada status 2xx; error menyimpan body error bertipe untuk response 4xx dan 5xx, berbentuk persis seperti yang dideklarasikan spesifikasi Anda; dan response adalah Response mentah untuk header dan status. Karena data dan error membentuk discriminated union, pemeriksaan truthiness mempersempit tipe, dan compiler memaksa Anda menangani jalur kegagalan sebelum bisa menyentuh payload sukses.

> Gunakan middleware alih-alih menulis ulang header di setiap lokasi pemanggilan. openapi-fetch mendukung hook middleware onRequest / onResponse, yang merupakan tempat bersih untuk melampirkan token auth, menyuntikkan request id, atau memusatkan logging. Ini menjaga lokasi pemanggilan tetap fokus pada data dan urusan auth berada tepat di satu file.

## Menjaga kedua sisi tetap sinkron di CI

Generasi hanya melindungi Anda jika benar-benar dijalankan. Mode kegagalan yang saya jaga adalah schema.d.ts basi yang di-commit berbulan-bulan lalu sementara API sudah berubah. Aturan saya pada layanan ter-deploy di VPS yang saya jalankan sederhana: spesifikasi diregenerasi di CI, dan jika file hasil regenerasi berbeda dari yang di-commit, build gagal. Satu pemeriksaan itu mengubah setiap perubahan backend yang diam-diam menjadi sinyal keras yang memblokir pada pull request.

> Tipe yang dihasilkan mendeskripsikan kontrak, bukan payload runtime. Jika server mengirim JSON yang rusak atau spesifikasinya bohong, TypeScript tetap memercayai tipe itu dan Anda mendapatkan objek yang mengklaim valid padahal tidak. Untuk batas yang tidak tepercaya atau kritis, validasi response saat runtime dengan pustaka skema seperti Zod. Tipe yang dihasilkan menghentikan penyimpangan di compile time; mereka tidak memverifikasi apa yang sebenarnya tiba melalui jaringan.

## Kapan pasangan ini adalah pilihan tepat

Jika Anda memiliki atau bisa memperoleh spesifikasi OpenAPI yang akurat, ini nyaris merupakan setup dengan usaha terendah dan imbalan tertinggi yang tersedia di ekosistem TypeScript. Ia unggul saat Anda menginginkan semantik fetch biasa tanpa keterikatan framework dan bundle yang bisa diukur dalam kilobyte. Jika sebaliknya Anda menginginkan hook TanStack Query yang dihasilkan atau SDK yang lebih lengkap dengan metode per operasi, generator yang lebih berat seperti Orval lebih cocok. Namun untuk kasus umum, frontend memanggil backend yang terdokumentasi, tipe yang dihasilkan ditambah klien bertipe tipis menghapus seluruh kategori bug dengan biaya satu langkah CI.

## Sources

- openapi-fetch official documentation
- openapi-typescript on GitHub
- openapi-fetch on npm

## Source links

- https://openapi-ts.dev/openapi-fetch/
- https://github.com/openapi-ts/openapi-typescript
- https://www.npmjs.com/package/openapi-fetch

## FAQ

### Apa perbedaan antara openapi-typescript dan openapi-fetch?

openapi-typescript adalah alat build-time yang membaca spesifikasi OpenAPI dan menghasilkan file tipe TypeScript tanpa kode runtime. openapi-fetch adalah klien runtime, sekitar enam kilobyte, yang mengonsumsi tipe yang dihasilkan itu untuk memberi Anda pembungkus bertipe penuh di atas fetch native. Anda memakainya bersama: satu menghasilkan tipe, yang lain menegakkannya di setiap lokasi pemanggilan.

### Apakah openapi-fetch menambah banyak ukuran bundle saya?

Tidak. openapi-fetch sekitar enam kilobyte dan merupakan pembungkus tipis di atas fetch native dengan overhead runtime yang praktis nol. Tipe yang dihasilkan openapi-typescript hanya ada di compile-time dan tidak mengirim apa pun ke browser, jadi total biaya runtime hanyalah klien kecil itu sendiri.

### Apakah tipe yang dihasilkan menggantikan validasi runtime seperti Zod?

Tidak. Tipe yang dihasilkan mendeskripsikan kontrak dalam spesifikasi, bukan byte sebenarnya yang dikirim server. Jika server mengembalikan data yang rusak atau spesifikasinya salah, TypeScript tetap memercayai tipe itu. Untuk endpoint yang tidak tepercaya atau kritis, validasi response saat runtime dengan pustaka seperti Zod sebagai tambahan tipe yang dihasilkan.

### Bagaimana cara menjaga tipe yang dihasilkan agar tidak basi?

Regenerasi tipe di CI dan gagalkan build jika file yang di-commit berbeda dari yang baru dihasilkan. Sebuah git diff dengan exit non-nol pada perubahan apa pun mengubah setiap perubahan kontrak backend yang diam-diam menjadi kegagalan pull-request yang memblokir, itulah yang menjaga frontend dan backend benar-benar sinkron.

### Bisakah saya menghasilkan tipe langsung dari backend NestJS yang berjalan?

Ya. openapi-typescript menerima URL, bukan hanya file lokal, jadi Anda bisa mengarahkannya ke dokumen JSON yang disajikan @nestjs/swagger, misalnya di /api-json. Ini menghapus langkah ekspor manual dan membuat CI meregenerasi langsung dari spesifikasi live yang sudah diterbitkan backend.

---

- HTML version: https://www.matthewswong.com/id/blog/openapi-typescript-end-to-end-types/
- English: https://www.matthewswong.com/en/blog/openapi-typescript-end-to-end-types/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
