Validator bermula dari pertanyaan paling mendasar: field mana yang wajib ada di setiap document? Kata kunci required menjawabnya dengan daftar nama field. Sederhana sekali kelihatannya — tetapi ada satu jebakan null yang membuat banyak orang kaget: required memeriksa kehadiran field, bukan tipe isinya. Chapter ini membedah keduanya.

Daftar Field Wajib #

Kita definisikan produk dengan tiga field wajib — nama, sku, harga — sementara kategori, stok, deskripsi, dan tanggal_ditambahkan boleh absen:

js
use latihan_validasi
db.produk.drop()
db.createCollection("produk", { validator: { $jsonSchema: {
  bsonType: "object",
  required: ["nama", "sku", "harga"],
  properties: {
    nama: { bsonType: "string", minLength: 3 },
    sku: { bsonType: "string", pattern: "^[A-Z]{3}-[0-9]{3}$" },
    harga: { bsonType: "number", minimum: 0 },
    kategori: { enum: ["Minuman", "Makanan", "Alat Tulis"] },
    stok: { bsonType: "int", minimum: 0 },
    deskripsi: { bsonType: ["string", "null"] },
    tanggal_ditambahkan: { bsonType: "date" }
  }
} } })
db.produk.insertOne({ nama: "Kopi Gayo 250g", sku: "MIN-001", harga: 85000, kategori: "Minuman", stok: 40, tanggal_ditambahkan: new Date("2026-01-10") })
db.produk.insertOne({ nama: "Teh Melati 100g", sku: "MIN-002", harga: 12000 })
db.produk.find()

Output yang Dihasilkan:

text
switched to db latihan_validasi
true
{ ok: 1 }
{
  acknowledged: true,
  insertedId: ObjectId('6ad39063519a8f4168bb439b')
}
{
  acknowledged: true,
  insertedId: ObjectId('6ad39063519a8f4168bb439c')
}
[
  {
    _id: ObjectId('6ad39063519a8f4168bb439b'),
    nama: 'Kopi Gayo 250g',
    sku: 'MIN-001',
    harga: 85000,
    kategori: 'Minuman',
    stok: 40,
    tanggal_ditambahkan: ISODate('2026-01-10T00:00:00.000Z')
  },
  {
    _id: ObjectId('6ad39063519a8f4168bb439c'),
    nama: 'Teh Melati 100g',
    sku: 'MIN-002',
    harga: 12000
  }
]

Document pertama lengkap, document kedua hanya berisi field wajib — keduanya lolos. Inilah pola wajib + opsional: field yang masuk daftar required wajib hadir, field yang hanya disebut di properties dijaga tipenya bila ada. Perhatikan juga kedua document punya _id otomatis: _id selalu dibuat server dan tidak pernah perlu dimasukkan ke required.

Menyisihkan satu field wajib langsung terlihat oleh server. Coba kirim produk tanpa sku:

js
db.produk.insertOne({ nama: "Gula Pasir 1kg", harga: 15000 })

Output yang Dihasilkan:

text
Uncaught:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId('6ad39063519a8f4168bb439d'),
  details: {
    operatorName: '$jsonSchema',
    schemaRulesNotSatisfied: [
      {
        operatorName: 'required',
        specifiedAs: { required: [ 'nama', 'sku', 'harga' ] },
        missingProperties: [ 'sku' ]
      }
    ]
  }
}

Galatnya berbeda dari semua yang kita lihat sejauh ini: operatorName: 'required' dengan missingProperties: [ 'sku' ] — nama field yang absen disebut satu per satu.

Jebakan: required Tanpa bsonType Melewatkan null #

Sekarang jebakannya. required hanya bertanya “apakah fieldnya ada?” — dan field bernilai null dianggap ada. Kita buat collection kedua yang hanya memakai required tanpa properties sama sekali:

js
db.produk2.drop()
db.createCollection("produk2", { validator: { $jsonSchema: {
  bsonType: "object",
  required: ["nama", "harga"]
} } })
db.produk2.insertOne({ nama: null, harga: null })
db.produk2.find()

Output yang Dihasilkan:

text
true
{ ok: 1 }
{
  acknowledged: true,
  insertedId: ObjectId('6ad39063519a8f4168bb439e')
}
[
  { _id: ObjectId('6ad39063519a8f4168bb439e'), nama: null, harga: null }
]

Insert lolos, dan hasil find() memperlihatkan document dengan kedua field wajib bernilai null. Bagi required, dokumen { nama: null, harga: null } memenuhi kontrak: fieldnya hadir semua.

Kita perbaiki produk2 dengan menambahkan properties lewat collMod, lalu kirim document yang sama:

js
db.runCommand({ collMod: "produk2", validator: { $jsonSchema: {
  bsonType: "object",
  required: ["nama", "harga"],
  properties: {
    nama: { bsonType: "string" },
    harga: { bsonType: "number", minimum: 0 }
  }
} } }).ok
db.produk2.insertOne({ nama: null, harga: null })

Output yang Dihasilkan:

text
1
Uncaught:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId('6ad39063519a8f4168bb439f'),
  details: {
    operatorName: '$jsonSchema',
    schemaRulesNotSatisfied: [
      {
        operatorName: 'properties',
        propertiesNotSatisfied: [
          {
            propertyName: 'nama',
            details: [
              {
                operatorName: 'bsonType',
                specifiedAs: { bsonType: 'string' },
                reason: 'type did not match',
                consideredValue: null,
                consideredType: 'null'
              }
            ]
          },
          {
            propertyName: 'harga',
            details: [
              {
                operatorName: 'bsonType',
                specifiedAs: { bsonType: 'number' },
                reason: 'type did not match',
                consideredValue: null,
                consideredType: 'null'
              }
            ]
          }
        ]
      }
    ]
  }
}

Kali ini ditolak. Bagian required lolos (kedua field ada), tetapi properties menemukan dua pelanggaran bsonType sekaligus: nama bernilai null padahal menuntut string, dan harga bernilai null padahal menuntut number. Gabungan required + bsonType barulah kontrak yang utuh.

Bersihkan database latihan:

js
db.dropDatabase()

Output yang Dihasilkan:

text
{ ok: 1, dropped: 'latihan_validasi' }

Rangkuman #

  • required: ["nama", "sku", "harga"] mewajibkan kehadiran field — bukan tipe atau isinya.
  • Field opsional cukup disebut di properties; aturannya berlaku hanya saat field tersebut ada.
  • _id dibuat otomatis oleh server dan tidak pernah perlu masuk daftar required.
  • Jebakan null: field bernilai null dianggap hadir, jadi required tanpa bsonType membiarkannya lolos.
  • Pasangan required + properties dengan bsonType membentuk kontrak lengkap: hadir dan bertipe benar.

Selanjutnya kita naik satu tingkat: menggabungkan banyak aturan dalam satu validator, memvalidasi isi array, dan menutup field yang tidak dikenal.