nik-id
v1.0.1
Published
Parser, validator, dan generator NIK (Nomor Induk Kependudukan) Indonesia — TypeScript, zero dependencies
Maintainers
Readme
nik-id
Parser, validator, dan generator NIK (Nomor Induk Kependudukan) Indonesia.
TypeScript-first, zero dependencies, 100% test coverage, dual ESM+CJS, tree-shakeable.
npm install nik-idTidak perlu database, API call, atau file eksternal. Cukup install dan langsung pakai.
Fitur
- Validasi format -- cek apakah NIK valid (panjang, digit, provinsi, tanggal lahir, nomor urut)
- Parse komponen -- ekstrak kode wilayah, tanggal lahir, gender, dan nomor urut dari NIK
- Generate NIK -- buat NIK yang valid secara format untuk testing, seeding, atau demo
- Discriminated union -- return type aman di-narrow pakai
result.valid(TypeScript-friendly) - Error bahasa Indonesia -- semua pesan error dalam bahasa Indonesia
- Tree-shakeable -- import hanya fungsi yang dibutuhkan via sub-path
- TypeScript-first -- strict types, auto-complete di IDE
- Dual ESM + CJS -- support semua environment (browser, Node.js, Bun, Deno)
- Zero dependencies -- tidak ada dependency runtime sama sekali
- 100% coverage -- statements, branches, functions, lines
Struktur NIK
NIK (Nomor Induk Kependudukan) adalah nomor identitas penduduk Indonesia yang terdiri dari 16 digit angka:
3204 07 650885 0001
│ │ │ └── Nomor urut registrasi (4 digit, 0001-9999)
│ │ └── Tanggal lahir DDMMYY — perempuan: DD + 40 (jadi 41-71)
│ └── Kode kecamatan (2 digit, format Kemendagri)
└── Kode kabupaten/kota (4 digit, format Kemendagri)| Posisi | Digit | Keterangan | Contoh |
|--------|-------|------------|--------|
| 1-2 | 2 digit | Kode provinsi Kemendagri (11-97) | 32 = Jawa Barat |
| 3-4 | 2 digit | Kode kabupaten/kota | 04 = Kab. Bandung |
| 5-6 | 2 digit | Kode kecamatan | 07 = Nagreg |
| 7-8 | 2 digit | Tanggal lahir (DD), perempuan +40 | 65 = tanggal 25 (perempuan) |
| 9-10 | 2 digit | Bulan lahir (MM) | 08 = Agustus |
| 11-12 | 2 digit | Tahun lahir (YY) | 85 = 1985 |
| 13-16 | 4 digit | Nomor urut registrasi | 0001 |
Catatan penting:
- NIK menggunakan kode Kemendagri (Kementerian Dalam Negeri), bukan BPS
- Perempuan: tanggal lahir ditambah 40 (DD 01-31 → DD 41-71)
- Tahun 2 digit: disambiguasi berdasarkan tahun sekarang (YY > sekarang → 1900+YY, else 2000+YY)
Quick Start
import { validateNIK, parseNIK, generateNIK } from 'nik-id';
// Validasi — cek apakah NIK valid
const valid = validateNIK("3204076508850001");
console.log(valid); // { valid: true }
// Parse — ekstrak semua komponen
const parsed = parseNIK("3204076508850001");
if (parsed.valid) {
console.log(parsed.provinceCode); // "32"
console.log(parsed.regencyCode); // "3204"
console.log(parsed.districtCode); // "320407"
console.log(parsed.gender); // "F"
console.log(parsed.birthDate); // Date: 1985-08-25
console.log(parsed.sequenceNumber); // "0001"
}
// Generate — buat NIK untuk testing
const nik = generateNIK({ gender: "F", birthDate: new Date("1985-08-25") });
console.log(nik); // "xxxxxx6508850001" (wilayah random)API Reference
validateNIK(nik: string): ValidationResult
Validasi format NIK secara bertahap (fail-fast). Return discriminated union:
import { validateNIK } from 'nik-id';
// atau: import { validateNIK } from 'nik-id/validate';
// NIK valid
validateNIK("3204076508850001");
// → { valid: true }
// Panjang salah
validateNIK("123");
// → { valid: false, error: "NIK harus 16 digit" }
// Mengandung huruf
validateNIK("320407650885000A");
// → { valid: false, error: "NIK hanya boleh berisi angka" }
// Kode provinsi di luar range
validateNIK("0004076508850001");
// → { valid: false, error: "Kode provinsi tidak valid" }
// Tanggal tidak ada di kalender (31 Februari)
validateNIK("3204073102850001");
// → { valid: false, error: "Tanggal lahir tidak valid" }
// Nomor urut 0000
validateNIK("3204076508850000");
// → { valid: false, error: "Nomor urut tidak valid" }parseNIK(nik: string): NIKResult
Parse NIK menjadi komponen-komponennya. Memvalidasi terlebih dahulu — kalau tidak valid, return error.
import { parseNIK } from 'nik-id';
// atau: import { parseNIK } from 'nik-id/parse';
const result = parseNIK("3204076508850001");
if (result.valid) {
console.log(result.nik); // "3204076508850001"
console.log(result.provinceCode); // "32" — Jawa Barat
console.log(result.regencyCode); // "3204" — Kab. Bandung
console.log(result.districtCode); // "320407" — Nagreg
console.log(result.gender); // "F" — Perempuan
console.log(result.birthDate); // Date: 1985-08-25
console.log(result.sequenceNumber); // "0001"
} else {
console.log(result.error);
}Komponen yang diekstrak:
| Field | Tipe | Keterangan | Contoh |
|-------|------|------------|--------|
| nik | string | NIK asli yang di-parse | "3204076508850001" |
| provinceCode | string | Kode provinsi Kemendagri (2 digit) | "32" |
| regencyCode | string | Kode kabupaten/kota Kemendagri (4 digit) | "3204" |
| districtCode | string | Kode kecamatan Kemendagri (6 digit) | "320407" |
| birthDate | Date | Tanggal lahir (object Date) | Date(1985-08-25) |
| gender | "M" \| "F" | Jenis kelamin | "F" |
| sequenceNumber | string | Nomor urut registrasi (4 digit) | "0001" |
generateNIK(options?: GenerateOptions): string
Generate NIK yang valid secara format. Semua parameter opsional — yang tidak diisi akan di-random.
import { generateNIK } from 'nik-id';
// atau: import { generateNIK } from 'nik-id/generate';
// Full random
generateNIK();
// Gender spesifik
generateNIK({ gender: "F" });
generateNIK({ gender: "M" });
// Tanggal lahir spesifik
generateNIK({ birthDate: new Date("1985-08-25") });
// Gender + tanggal lahir
generateNIK({ gender: "F", birthDate: new Date("1985-08-25") });
// Wilayah spesifik (harus konsisten dari provinsi ke kecamatan)
generateNIK({
provinceCode: "32",
regencyCode: "3204",
districtCode: "320407",
});
// Semua opsi sekaligus
generateNIK({
provinceCode: "32",
regencyCode: "3204",
districtCode: "320407",
gender: "F",
birthDate: new Date("1985-08-25"),
});Opsi yang tersedia:
| Opsi | Tipe | Keterangan |
|------|------|------------|
| provinceCode | string? | Kode provinsi Kemendagri (2 digit, 11-97) |
| regencyCode | string? | Kode kabupaten/kota (4 digit, harus match provinsi) |
| districtCode | string? | Kode kecamatan (6 digit, harus match kabupaten) |
| gender | "M" \| "F"? | Jenis kelamin |
| birthDate | Date? | Tanggal lahir (kalau tidak diisi, random 1950-2005) |
Error yang di-throw:
// Kode provinsi di luar range
generateNIK({ provinceCode: "99" });
// → Error: "Kode provinsi tidak valid (harus 11-97)"
// Kode kabupaten tidak cocok dengan provinsi
generateNIK({ provinceCode: "32", regencyCode: "3301" });
// → Error: 'Kode kabupaten/kota "3301" tidak sesuai dengan kode provinsi "32"'
// Kode kecamatan tidak cocok dengan kabupaten
generateNIK({ provinceCode: "32", regencyCode: "3204", districtCode: "320501" });
// → Error: 'Kode kecamatan "320501" tidak sesuai dengan kode kabupaten/kota "3204"'Catatan: NIK yang dihasilkan valid secara format tapi bukan NIK asli milik orang sungguhan. Cocok untuk testing, seeding database, atau demo.
Validasi yang Dilakukan
validateNIK dan parseNIK melakukan pengecekan bertahap (fail-fast — berhenti di error pertama):
| # | Pengecekan | Detail |
|---|-----------|--------|
| 1 | Tipe data | Input harus bertipe string |
| 2 | Panjang | Harus tepat 16 karakter |
| 3 | Format | Harus semua digit angka (0-9) |
| 4 | Kode provinsi | 2 digit pertama harus dalam range 11-97 |
| 5 | Tanggal lahir | DD (01-31 atau 41-71), MM (01-12), valid di kalender |
| 6 | Nomor urut | Digit 13-16 tidak boleh 0000 |
Catatan: Validasi bersifat format-only. Package ini tidak mengecek apakah kode wilayah benar-benar terdaftar di database Kemendagri. Untuk lookup nama wilayah, gunakan package
kode-wilayah-id.
Error Messages
Semua pesan error dalam Bahasa Indonesia:
| Error | Penyebab | Contoh Input |
|-------|----------|-------------|
| NIK harus berupa string | Input bukan bertipe string | 12345, null, undefined |
| NIK harus 16 digit | Panjang bukan 16 karakter | "123", "" |
| NIK hanya boleh berisi angka | Mengandung karakter non-digit | "320407650885000A" |
| Kode provinsi tidak valid | 2 digit pertama di luar range 11-97 | "0004076508850001" |
| Tanggal lahir tidak valid | Tanggal/bulan invalid atau tidak ada di kalender | "3204073102850001" |
| Nomor urut tidak valid | 4 digit terakhir adalah 0000 | "3204076508850000" |
Tree-shaking / Sub-path Imports
Import hanya fungsi yang dibutuhkan — bundler hanya include kode yang di-import:
// Hanya validasi
import { validateNIK } from 'nik-id/validate';
// Hanya parsing
import { parseNIK } from 'nik-id/parse';
// Hanya generator
import { generateNIK } from 'nik-id/generate';
// Types only (zero runtime)
import type { NIKResult, ValidationResult, GenerateOptions } from 'nik-id/types';
// Atau import semua sekaligus
import { validateNIK, parseNIK, generateNIK } from 'nik-id';Bundle Size
Package ini sangat ringan karena zero dependencies dan hanya berisi logika validasi:
| Import | Ukuran (minified) |
|--------|-------------------|
| nik-id/validate | ~1.8 KB |
| nik-id/parse | ~2.8 KB |
| nik-id/generate | ~2.3 KB |
| nik-id (full) | ~5.2 KB |
Integrasi dengan kode-wilayah-id
Package ini tidak menyertakan data wilayah — hanya mengembalikan kode Kemendagri. Untuk resolve ke nama wilayah, gunakan package kode-wilayah-id:
npm install kode-wilayah-idimport { parseNIK } from 'nik-id';
import {
getProvinceByKemendagriCode,
getRegencyByKemendagriCode,
getDistrictByKemendagriCode,
} from 'kode-wilayah-id';
const result = parseNIK("3204076508850001");
if (result.valid) {
const province = getProvinceByKemendagriCode(result.provinceCode);
const regency = getRegencyByKemendagriCode(result.regencyCode);
const district = getDistrictByKemendagriCode(result.districtCode);
console.log(province?.name); // "JAWA BARAT"
console.log(regency?.name); // "KAB. BANDUNG"
console.log(district?.name); // "NAGREG"
}Discriminated Union Pattern
Return type parseNIK dan validateNIK menggunakan discriminated union — TypeScript bisa narrow type otomatis berdasarkan result.valid:
import { parseNIK } from 'nik-id';
import type { NIKResult, NIKValid, NIKInvalid } from 'nik-id/types';
const result: NIKResult = parseNIK(input);
if (result.valid) {
// TypeScript tahu ini NIKValid — semua field tersedia
console.log(result.provinceCode); // ✅ string
console.log(result.gender); // ✅ "M" | "F"
console.log(result.birthDate); // ✅ Date
} else {
// TypeScript tahu ini NIKInvalid — hanya error
console.log(result.error); // ✅ string
// console.log(result.gender); // ❌ compile error!
}Pattern yang sama berlaku untuk validateNIK:
import { validateNIK } from 'nik-id';
const result = validateNIK(input);
if (result.valid) {
// ValidationValid — tidak ada field lain
console.log("NIK valid!");
} else {
// ValidationInvalid — ada field error
console.log(result.error);
}Contoh Penggunaan
Lihat folder examples/ untuk contoh lengkap di berbagai framework dan runtime:
| Framework | File | Fitur |
|-----------|------|-------|
| Node.js | node.ts | Basic usage — validasi, parse, generate |
| React | react.tsx | Form validasi KTP dengan feedback real-time |
| Next.js | nextjs.tsx | API route + server component |
| Express | express.ts | REST API endpoint validasi NIK |
| Hono | hono.ts | Lightweight REST API validasi NIK |
| Bun | bun.ts | Native Bun HTTP server |
| Deno | deno.ts | Native Deno server |
# Jalankan contoh Node.js
npx tsx examples/node.tsTypes
Semua type tersedia di nik-id/types:
import type {
NIKValid,
NIKInvalid,
NIKResult,
ValidationValid,
ValidationInvalid,
ValidationResult,
GenerateOptions,
} from 'nik-id/types';NIKResult (parseNIK)
// Hasil valid
interface NIKValid {
valid: true;
nik: string; // "3204076508850001"
provinceCode: string; // "32"
regencyCode: string; // "3204"
districtCode: string; // "320407"
birthDate: Date; // Date(1985-08-25)
gender: "M" | "F"; // "F"
sequenceNumber: string; // "0001"
}
// Hasil invalid
interface NIKInvalid {
valid: false;
error: string; // "NIK harus 16 digit"
}
type NIKResult = NIKValid | NIKInvalid;ValidationResult (validateNIK)
interface ValidationValid {
valid: true;
}
interface ValidationInvalid {
valid: false;
error: string;
}
type ValidationResult = ValidationValid | ValidationInvalid;GenerateOptions (generateNIK)
interface GenerateOptions {
provinceCode?: string; // "32" (2 digit, 11-97)
regencyCode?: string; // "3204" (4 digit, harus match provinsi)
districtCode?: string; // "320407" (6 digit, harus match kabupaten)
gender?: "M" | "F"; // Jenis kelamin
birthDate?: Date; // Tanggal lahir (default: random 1950-2005)
}Encoding Tanggal Lahir
NIK meng-encode tanggal lahir dalam 6 digit (posisi 7-12) dengan format DDMMYY:
Gender dan DD
| Gender | Range DD | Contoh | Arti |
|--------|----------|--------|------|
| Laki-laki | 01-31 | DD=15 | Lahir tanggal 15 |
| Perempuan | 41-71 | DD=65 | Lahir tanggal 25 (65-40=25) |
| Invalid | 00, 32-40, 72-99 | -- | Tidak valid |
Disambiguasi Tahun (YY)
Karena NIK hanya menyimpan 2 digit tahun, perlu disambiguasi:
| YY | Tahun sekarang (2 digit) | Hasil | Logika | |----|--------------------------|-------|--------| | 85 | 26 | 1985 | 85 > 26 → 1900 + 85 | | 02 | 26 | 2002 | 02 ≤ 26 → 2000 + 02 | | 26 | 26 | 2026 | 26 ≤ 26 → 2000 + 26 | | 27 | 26 | 1927 | 27 > 26 → 1900 + 27 | | 00 | 26 | 2000 | 00 ≤ 26 → 2000 + 00 | | 99 | 26 | 1999 | 99 > 26 → 1900 + 99 |
Kode Wilayah Kemendagri
NIK menggunakan kode wilayah format Kemendagri (bukan BPS):
| Level | Panjang | Contoh | Keterangan |
|-------|---------|--------|------------|
| Provinsi | 2 digit | "32" | Jawa Barat |
| Kabupaten/Kota | 4 digit | "3204" | Kab. Bandung |
| Kecamatan | 6 digit | "320407" | Nagreg |
Range kode provinsi yang valid: 11 (Aceh) sampai 97 (Papua Barat Daya).
Package ini hanya memvalidasi range kode provinsi (11-97), tidak mengecek apakah kode spesifik benar-benar ada. Untuk lookup nama wilayah, gunakan
kode-wilayah-id.
Contributing
Kontribusi sangat diterima!
git clone https://github.com/sumitroajiprabowo/nik-id.git
cd nik-id
npm install
npm run lint # Lint check (Biome)
npm run format:check # Format check
npm run typecheck # TypeScript check
npm run test:coverage # Test dengan coverage (harus 100%)
npm run build # Build ESM + CJSChangelog
Lihat CHANGELOG.md untuk riwayat perubahan.
