Claude Code dan OpenAPI 3.1: Validasi Swagger serta Generasi TypeScript
Buat spesifikasi OpenAPI 3.1 dengan Claude Code, validasi, lalu hasilkan client TypeScript yang aman.
Claude Code bisa membuat file OpenAPI dengan cepat. Masalahnya, spesifikasi yang terlihat rapi belum tentu benar. Ia dapat menebak field dari label UI, menambahkan nilai enum yang tidak ada di database, atau mengubah format error dari satu endpoint ke endpoint lain.
Panduan ini memakai Claude Code sebagai asisten pemelihara kontrak API, bukan pembuat schema yang selalu benar. Kita akan membuat spesifikasi OpenAPI 3.1, memvalidasinya, menghasilkan client TypeScript dan server stub, lalu menambahkan pemeriksaan kecil untuk menangkap schema halusinasi. OpenAPI menjelaskan endpoint REST, request, response, autentikasi, dan error dalam format yang bisa dibaca tool. Swagger adalah ekosistem tool yang umum dipakai untuk mengedit, melihat, dan meninjau dokumen tersebut.
Masa menguji alur ini pada API pesanan sederhana. Prompt bebas seperti “baca repo dan buat OpenAPI” menghasilkan status cancelled yang masuk akal tetapi tidak ada di domain model. Hasilnya jauh lebih stabil ketika prompt menentukan file yang wajib dibaca, melarang field yang tidak terbukti, menjalankan validasi, dan memisahkan pertanyaan terbuka dari schema publik.
Gunakan rujukan resmi. Pada 3 Juni 2026, OpenAPI Specification latest sudah 3.2.0, tetapi contoh di artikel ini tetap memakai openapi: 3.1.0 demi kompatibilitas generator. Untuk detail 3.1, pakai OpenAPI Specification 3.1.2, Swagger Editor documentation, OpenAPI Generator usage, typescript-fetch generator, dan typescript-nestjs-server generator. Untuk OpenAPI 3.1, cek dukungan Swagger Editor Next di dokumentasi terbaru.
Baca juga panduan pengembangan API, panduan testing API, praktik keamanan, dan praktik CLAUDE.md.
flowchart LR
A["Implementasi, DB, test"] --> B["Claude Code menyusun OpenAPI"]
B --> C["Validasi Swagger dan CLI"]
C --> D["Client TypeScript"]
C --> E["Contract tests"]
C --> F["Security review"]
D --> G["Frontend atau partner"]
E --> H["CI publish gate"]
F --> H
Kapan dipakai
| Kebutuhan | Claude Code mengerjakan | Tim memutuskan |
|---|---|---|
| Mendokumentasikan API lama | Route, model, status HTTP, batas auth | Field publik, kompatibilitas, penamaan |
| Contract-first API | Draft OpenAPI 3.1, contoh, command | Aturan bisnis, kebijakan error, scope |
| Integrasi frontend | Client TypeScript, contoh panggilan | UX, retry, pesan pengguna |
| Contract tests | operationId, response 4xx, enum, auth | Kebijakan breaking change dan pengecualian |
| Security review | Schema publik, auth, kandidat PII, ID internal | Risiko legal, audit, janji ke partner |
| API untuk partner | Deskripsi Swagger, contoh, auth | Kontrak, rate limit, dukungan |
Aturan utamanya: detail schema yang tidak dibuktikan oleh kode, database, test, atau keputusan produk tidak boleh masuk ke schema publik.
Setup yang bisa disalin
Gunakan Node.js 20+. OpenAPI Generator memakai Java untuk proses generasi kode.
mkdir openapi-claude-demo
cd openapi-claude-demo
npm init -y
npm install -D @openapitools/openapi-generator-cli js-yaml
mkdir specs generated scripts
package.json:
{
"type": "module",
"scripts": {
"validate:openapi": "openapi-generator-cli validate -i specs/openapi.yaml",
"lint:contract": "node scripts/check-openapi-rules.mjs",
"test:contract": "node --test scripts/contract.test.mjs",
"generate:client": "openapi-generator-cli generate -i specs/openapi.yaml -g typescript-fetch -o generated/client --additional-properties=supportsES6=true,npmName=@example/orders-client,npmVersion=0.1.0",
"generate:server": "openapi-generator-cli generate -i specs/openapi.yaml -g typescript-nestjs-server -o generated/server",
"contract": "npm run validate:openapi && npm run lint:contract && npm run test:contract && npm run generate:client"
},
"devDependencies": {
"@openapitools/openapi-generator-cli": "latest",
"js-yaml": "latest"
}
}
specs/openapi.yaml:
openapi: 3.1.0
info:
title: Orders API
version: 0.1.0
description: Contract-first sample API for order intake.
servers:
- url: https://api.example.com/v1
paths:
/orders:
post:
operationId: createOrder
summary: Create an order
tags: [orders]
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateOrderRequest"
responses:
"201":
description: Order created
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
responses:
BadRequest:
description: Invalid request
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
Unauthorized:
description: Missing or invalid token
schemas:
CreateOrderRequest:
type: object
additionalProperties: false
required: [customerEmail, items]
properties:
customerEmail:
type: string
format: email
note:
type: [string, "null"]
items:
type: array
minItems: 1
items:
$ref: "#/components/schemas/OrderItemInput"
OrderItemInput:
type: object
additionalProperties: false
required: [sku, quantity]
properties:
sku:
type: string
quantity:
type: integer
minimum: 1
Order:
type: object
additionalProperties: false
required: [id, customerEmail, status, items, createdAt]
properties:
id:
type: string
format: uuid
customerEmail:
type: string
format: email
status:
type: string
enum: [pending, paid]
items:
type: array
items:
$ref: "#/components/schemas/OrderItemInput"
createdAt:
type: string
format: date-time
ErrorResponse:
type: object
additionalProperties: false
required: [code, message]
properties:
code:
type: string
enum: [invalid_request, unauthorized]
message:
type: string
scripts/check-openapi-rules.mjs:
import { readFileSync } from "node:fs";
const spec = readFileSync("specs/openapi.yaml", "utf8");
const forbidden = [
{ pattern: /\bTBD\b|\bTODO\b|placeholder/i, reason: "unfinished placeholder" },
{ pattern: /nullable:\s*true/, reason: "OpenAPI 3.1 should use JSON Schema null types" },
{ pattern: /password|secret|clientSecret|apiKey/i, reason: "review sensitive fields before publishing" }
];
const errors = forbidden.filter((rule) => rule.pattern.test(spec)).map((rule) => `- ${rule.reason}`);
if (!spec.includes("additionalProperties: false")) errors.push("- schemas should explicitly decide additionalProperties");
if (errors.length > 0) {
console.error("Contract review failed:\n" + errors.join("\n"));
process.exit(1);
}
console.log("Contract review passed.");
Tambahkan contract test yang bisa berjalan di CI. Ini bukan pengganti integration test ke server nyata; tujuannya memeriksa kontrak publik: operationId, security untuk operasi mutasi, response 4xx bersama, dan enum Order.status saat ini.
// scripts/contract.test.mjs
import { readFileSync } from "node:fs";
import assert from "node:assert/strict";
import test from "node:test";
import { load } from "js-yaml";
const doc = load(readFileSync("specs/openapi.yaml", "utf8"));
const httpMethods = new Set(["get", "put", "post", "delete", "patch", "options", "head", "trace"]);
function operations() {
return Object.entries(doc.paths ?? {}).flatMap(([path, pathItem]) =>
Object.entries(pathItem)
.filter(([method]) => httpMethods.has(method))
.map(([method, operation]) => ({ path, method, operation }))
);
}
test("operationId is unique and camelCase", () => {
const ids = operations().map(({ operation }) => operation.operationId);
assert.equal(new Set(ids).size, ids.length);
for (const id of ids) assert.match(id, /^[a-z][A-Za-z0-9]*$/);
});
test("mutating operations require security", () => {
for (const { path, method, operation } of operations()) {
if (["get", "head", "options"].includes(method)) continue;
assert.ok(operation.security?.length, `${method.toUpperCase()} ${path} must declare security`);
}
});
test("client-visible 4xx responses reuse shared components", () => {
for (const { path, method, operation } of operations()) {
for (const [status, response] of Object.entries(operation.responses ?? {})) {
if (!status.startsWith("4")) continue;
assert.ok(response.$ref?.startsWith("#/components/responses/"), `${method.toUpperCase()} ${path} ${status}`);
}
}
});
test("Order status enum matches the current implementation decision", () => {
const status = doc.components.schemas.Order.properties.status;
assert.deepEqual(status.enum, ["pending", "paid"]);
});
Jalankan:
npm run validate:openapi
npm run lint:contract
npm run test:contract
npm run generate:client
npm run generate:server
Prompt untuk Claude Code
Buat kontrak OpenAPI 3.1 untuk API pesanan.
Baca src/routes/orders.ts, src/domain/order.ts, prisma/schema.prisma, dan tests/orders.test.ts terlebih dahulu.
Jangan membuat field, enum, atau kode error yang tidak dibuktikan oleh kode, schema, test, atau catatan produk.
Masukkan pertanyaan terbuka ke x-claude-review, bukan ke schemas publik.
Jangan gunakan nullable: true. Gunakan operationId camelCase yang stabil.
Deklarasikan security untuk operasi terlindungi. Field internal, PII, dan kandidat secret masuk ke x-claude-review sebelum schema publik.
Jalankan npm run validate:openapi, npm run lint:contract, dan npm run test:contract, lalu perbaiki kegagalan.
Kembalikan diff, contoh pemanggilan client TypeScript, catatan security review, dan pertanyaan untuk review manusia.
Contract tests dan security review
Untuk API SaaS atau partner, review OpenAPI bukan sekadar memperbaiki teks. Pisahkan menjadi spec review, review client hasil generate, contract tests, dan security review. Spec review melihat path, method, schema, status, dan contoh. Review client melihat tipe typescript-fetch serta null/optional. Contract tests menahan auth yang hilang, bentuk error yang berubah, dan enum yang rusak. Security review memeriksa field publik, PII, ID internal, URL server, dan kemungkinan secret.
Claude Code bisa menyusun daftar diff, draft test, dan tabel review. Tim tetap harus memutuskan field mana yang boleh dipublikasikan, rate limit apa yang dijanjikan, risiko legal apa yang ada, dan bukti audit apa yang perlu disimpan.
Kesalahan umum
Kesalahan pertama adalah schema halusinasi seperti cancelled, refunded, atau adminNote. Kedua, format error tidak konsisten. Ketiga, kebiasaan OpenAPI 3.0 seperti nullable: true masih dipakai di file 3.1. Keempat, kode hasil generate diedit manual. Kelima, review hanya memakai tampilan Swagger tanpa memeriksa auth, operationId, contoh response, dan kompatibilitas enum.
CTA training dan konsultasi
Perapihan OpenAPI cocok untuk training dan konsultasi karena output-nya jelas: spesifikasi, client TypeScript, server stub, contract test, validasi, dan checklist CI. Solo builder bisa mulai dari produk ClaudeCodeLab untuk prompt dan template review. Tim yang perlu menyesuaikan ini ke repository nyata, CLAUDE.md, testing API, dan security review bisa memakai training dan konsultasi Claude Code.
Hasil uji langsung
Dalam pengujian Masa, prompt bebas menambahkan status yang tidak diimplementasikan dan mengubah bentuk error. Setelah file wajib, aturan tidak boleh mengarang, openapi-generator-cli validate, script lokal, dan node --test diterapkan, review manusia berfokus pada empat hal: field publik, masa depan enum, teks error autentikasi, dan apakah data internal bocor ke schema. OpenAPI paling berguna sebagai kontrak bersama antara tim dan AI, bukan dokumen yang diserahkan begitu saja.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.