Tanpa additionalProperties, validasi skema tidak memvalidasi apa pun
Intinya: Validasi yang lulus bukan berarti data bersih. Pydantic secara bawaan mengabaikan kolom tak dikenal, Zod menghapusnya, Ajv membiarkannya — parameter halusinasi dan kunci salah ketik lolos tanpa suara. Artikel ini menunjukkan tiga bentuk penerimaan diam dan memberikan resep lengkap: menutup objek, mengontrol koersi, mengencangkan setiap tingkat sarang.

Kesimpulan: secara bawaan, validator mengabaikan kolom yang tidak dideklarasikan alih-alih menolaknya. Tanpa additionalProperties: false, validasi yang lulus hanya membuktikan kolom yang dideklarasikan tidak bermasalah.
Perilaku paling berbahaya dari validator bukanlah memunculkan error, melainkan diam. Kunci salah ketik atau parameter yang dihalusinasi model melewati validasi tanpa suara dan meledak di logika bisnis.
Satu: validasi yang "lulus"
{
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["order_id", "amount"]
}
Kirim {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — kunci ketiga salah ketik. Dengan konfigurasi bawaan Ajv, hasilnya lulus. Kolom ekstra tidak ditolak maupun ditandai.
Dalam spesifikasi JSON Schema, nilai bawaan additionalProperties adalah true: kolom yang tidak dideklarasikan diizinkan.
Dua: tiga rupa keheningan
| Runtime | Perilaku bawaan | Akibat |
|---|---|---|
| Ajv (JS) | membiarkan lewat | data kotor sampai ke logika |
Zod z.object |
menghapus | kolom hilang, objek memendek |
| Pydantic v2 | mengabaikan (extra="ignore") |
sama, tanpa jejak di log |
Penghapusan lebih licik daripada meloloskan. Kedua pustaka gagal secara diam dengan cara yang sama, secara bawaan.
Tiga: pemanggilan alat LLM memperbesar masalah
Argumen dibuat oleh model: ia dengan percaya diri mengarang nama kolom dan mengirim "3" sebagai angka. Platform mendefinisikan parameter sebagai JSON Schema tetapi secara bawaan tidak memaksa skema ditutup — mode strict harus diaktifkan eksplisit, dan batasan tata bahasa hanya berlaku pada keluaran langsung model.
{
"order_id": 4411,
"refund_amount_usd": 50,
"reason_code": "damaged",
"urgent": "yes"
}
Dengan objek tertutup, reason_code dan urgent ditolak dan error menunjuk ke jalur /reason_code.
Empat: cara menutup
Ajv: tambahkan additionalProperties: false pada node objek.
import Ajv from "ajv";
const ajv = new Ajv({ allErrors: true });
const schema = {
type: "object",
properties: { order_id: { type: "string" }, amount: { type: "number" } },
required: ["order_id", "amount"],
additionalProperties: false,
};
const validate = ajv.compile(schema);
validate({ order_id: "A1", amount: 50, reasn_code: "x" }); // false
Zod: ganti z.object dengan z.strictObject atau rantai .strict(). Pydantic: konfigurasikan ConfigDict(extra="forbid").
Aturan bersama: penutupan dilakukan per tingkat. Setiap objek bersarang butuh additionalProperties: false sendiri.
Lima: kebijakan koersi
Mode lax Pydantic mengubah "5" menjadi 5. Tiga kebijakan: ketat (tolak tipe yang salah), terkontrol (hanya konversi eksplisit berbatas), bebas (hanya data internal). Untuk input yang tidak dipercaya, pilih 1 atau 2 dan tuliskan dalam pengujian.
Enam: daftar periksa sarang
| Titik | Tindakan |
|---|---|
| Objek akar | additionalProperties: false |
| Setiap objek bersarang | sama |
| Elemen larik | tutup objek dalam items |
| Enum dan rentang | tambahkan enum, minimum/maximum |
| Skema hasil generator | lengkapi secara manual |
Tujuh: setelah penolakan
allErrors: true melaporkan semua kolom sekaligus; verbose: true memberikan jalur. Masalah sintaks ditangkap parser sebelum validasi skema.
FAQ
Terlalu ketat?
Tergantung kontrak. Untuk input yang Anda definisikan sendiri, tutup; untuk data pihak ketiga yang bisa diperluas, tutup hanya inti dan pisahkan area ekstensi.
Strip milik Zod bukannya lebih aman?
Tidak: ia melindungi hilir dengan mengorbankan pemanggil. Error harus muncul cepat dan eksplisit.
Apakah mode strict platform menggantikan validasi lokal?
Tidak. Skema sendiri bisa terbuka, dan tata bahasa mungkin tidak mencakup argumen alat.
Setelah menutup, data lama gagal secara massal?
Itulah yang memang harus terungkap. Inventarisasi dengan allErrors, tambahkan kolom yang tidak berbahaya, perbaiki yang berbahaya.
Solusi satu baris?
Tidak ada, tetapi langkah minimal: pastikan setiap type: "object" disertai additionalProperties: false.