Penanganan Error

Teknik defensive programming untuk menangani kondisi tak terduga dan mencegah program crash di NusaScript.

Strategi Penanganan Error

NusaScript menggunakan tiga pendekatan utama untuk penanganan error:

PendekatanKapan Digunakan
coba/tangkapOperasi yang bisa gagal (database, file, parsing)
lemparMelempar error dari fungsi Anda
Cek nilai nihilHasil query database, fungsi yang bisa gagal
Validasi inputSebelum memproses data dari pengguna

Blok coba / tangkap

Gunakan coba untuk menjalankan kode yang mungkin menghasilkan error, dan tangkap untuk menangani error tersebut.

Contoh — coba/tangkap Dasar
coba {
    tampilkan("Mencoba operasi yang aman...")
    buat x = 10 + 5
    tampilkan("Hasil: " + x)
} tangkap(err) {
    tampilkan("ERROR: " + err)
}
Output
Mencoba operasi yang aman...
Hasil: 15

Melempar Error — lempar

Gunakan lempar untuk membuat error baru. Error akan ditangkap oleh blok tangkap terdekat.

Contoh — Lempar dan Tangkap
coba {
    tampilkan("Mencoba koneksi DB...")
    lempar "Koneksi database terputus!"
    tampilkan("Baris ini TIDAK dijalankan")
} tangkap(err) {
    tampilkan("Ditangkap: " + err)
}
Output
Mencoba koneksi DB...
Ditangkap: Koneksi database terputus!

Blok akhirnya

Blok akhirnya akan selalu dijalankan, baik ada error maupun tidak. Berguna untuk cleanup.

Contoh — Dengan akhirnya
coba {
    lempar "Terjadi kesalahan!"
} tangkap(e) {
    tampilkan("Error: " + e)
} akhirnya {
    tampilkan("Cleanup selesai")  // Selalu dijalankan
}
Output
Error: Terjadi kesalahan!
Cleanup selesai

Lempar Error dari Fungsi

Contoh — Validasi dalam Fungsi
fungsi bagi(a, b) {
    jika b == 0 {
        lempar "Tidak bisa membagi dengan nol!"
    }
    kembalikan a / b
}

coba {
    tampilkan(bagi(10, 0))
} tangkap(e) {
    tampilkan(e)  // "Tidak bisa membagi dengan nol!"
}

Nested coba/tangkap

Contoh — Bersarang
coba {
    coba {
        lempar "Error di dalam"
    } tangkap(e1) {
        tampilkan("Tangkap dalam: " + e1)
        lempar "Error diteruskan dari tangkap"
    }
} tangkap(e2) {
    tampilkan("Tangkap luar: " + e2)
}
Output
Tangkap dalam: Error di dalam
Tangkap luar: Error diteruskan dari tangkap
KeywordFungsiPadanan
cobaBlok kode yang mungkin errortry
tangkap(err)Menangkap dan handle errorcatch
akhirnyaSelalu dijalankan (opsional)finally
lemparMelempar error baruthrow

Cek Nilai Nihil

Banyak fungsi NusaScript mengembalikan nihil ketika operasi gagal atau tidak menemukan data.

Contoh — Cek Hasil Database
buat db   = db_buka("sqlite", "app.db")
buat user = db_ambil_satu(db, "SELECT * FROM pengguna WHERE id=?", 999)

// Cek apakah data ditemukan
jika user == nihil {
    tampilkan("Pengguna tidak ditemukan!")
    keluar(1)
}

tampilkan("Nama:", ambil(user, "nama"))
tampilkan("Email:", ambil(user, "email"))

Validasi Input API

Contoh — Validasi Berlapis
fungsi validasi_email(email) {
    jika email == nihil atau email == "" {
        kembalikan "Email tidak boleh kosong"
    }
    jika !muat(email, "@") {
        kembalikan "Format email tidak valid"
    }
    jika panjang(email) < 5 {
        kembalikan "Email terlalu pendek"
    }
    kembalikan nihil  // tidak ada error
}

fungsi validasi_sandi(sandi) {
    jika sandi == nihil atau sandi == "" {
        kembalikan "Sandi tidak boleh kosong"
    }
    jika panjang(sandi) < 8 {
        kembalikan "Sandi minimal 8 karakter"
    }
    kembalikan nihil
}

// Gunakan di handler
tambah_rute(app, "POST", "/daftar", fungsi(req) {
    buat b     = body_json(req)
    buat email = ambil(b, "email")
    buat sandi = ambil(b, "sandi")
    buat nama  = ambil(b, "nama")

    // Validasi satu per satu
    buat err_email = validasi_email(email)
    jika err_email != nihil {
        kembalikan ke_json({"error": err_email})
    }

    buat err_sandi = validasi_sandi(sandi)
    jika err_sandi != nihil {
        kembalikan ke_json({"error": err_sandi})
    }

    jika nama == nihil atau panjang(nama) < 2 {
        kembalikan ke_json({"error": "Nama minimal 2 karakter"})
    }

    // Semua valid, proses pendaftaran
    buat hash = hash_sandi(sandi)
    buat db   = db_buka("sqlite", "app.db")
    db_query(db, "INSERT INTO pengguna (nama, email, sandi_hash) VALUES (?, ?, ?)",
        nama, email, hash)

    kembalikan ke_json({"sukses": benar, "pesan": "Pendaftaran berhasil"})
})

Guard Clause Pattern

Kembalikan lebih awal (early return) untuk mengurangi nesting dan membuat kode lebih mudah dibaca.

Contoh — Early Return
// ❌ Buruk — banyak nesting
fungsi proses_buruk(req) {
    buat token = header_req(req, "Authorization")
    jika token != "" {
        buat klaim = jwt_verifikasi(token, "rahasia")
        jika klaim != nihil {
            buat id   = ambil(klaim, "id")
            buat db   = db_buka("sqlite", "app.db")
            buat user = db_ambil_satu(db, "SELECT * FROM pengguna WHERE id=?", id)
            jika user != nihil {
                kembalikan ke_json(user)
            }
        }
    }
    kembalikan ke_json({"error": "Gagal"})
}

// ✅ Bagus — guard clause (early return)
fungsi proses_bagus(req) {
    buat token = header_req(req, "Authorization")
    jika token == "" {
        kembalikan ke_json({"error": "Token diperlukan"})
    }

    buat klaim = jwt_verifikasi(token, "rahasia")
    jika klaim == nihil {
        kembalikan ke_json({"error": "Token tidak valid"})
    }

    buat id   = ambil(klaim, "id")
    buat db   = db_buka("sqlite", "app.db")
    buat user = db_ambil_satu(db, "SELECT * FROM pengguna WHERE id=?", id)
    jika user == nihil {
        kembalikan ke_json({"error": "Pengguna tidak ditemukan"})
    }

    kembalikan ke_json(user)
}

Defensive Programming untuk Array

Contoh — Cek Panjang Array
buat db   = db_buka("sqlite", "app.db")
buat rows = db_ambil_semua(db, "SELECT * FROM produk WHERE kategori_id=?", 5)

// Selalu cek panjang sebelum akses indeks
jika panjang(rows) == 0 {
    tampilkan("Tidak ada produk dalam kategori ini")
    keluar(0)
}

buat pertama = rows[0]
tampilkan("Produk pertama:", ambil(pertama, "nama"))

// Loop dengan aman
buat i = 0
selama i < panjang(rows) {
    buat r = rows[i]
    tampilkan(i + 1, ".", ambil(r, "nama"), "-", format_rupiah(ambil(r, "harga")))
    i += 1
}

Error Response Standar API

Contoh — Response Error Konsisten
// Definisikan helper response sekali, gunakan di mana-mana
fungsi sukses(data) {
    kembalikan ke_json({
        "sukses"    : benar,
        "data"      : data,
        "timestamp" : format_waktu(waktu_sekarang())
    })
}

fungsi gagal(pesan, kode) {
    kembalikan ke_json({
        "sukses"    : salah,
        "error"     : pesan,
        "kode"      : kode,
        "timestamp" : format_waktu(waktu_sekarang())
    })
}

// Penggunaan
tambah_rute(app, "GET", "/api/pengguna", fungsi(req) {
    buat token = header_req(req, "Authorization")
    jika token == "" {
        kembalikan gagal("Token autentikasi diperlukan", 401)
    }

    buat klaim = jwt_verifikasi(token, "rahasia")
    jika klaim == nihil {
        kembalikan gagal("Token tidak valid atau kedaluwarsa", 403)
    }

    buat db   = db_buka("sqlite", "app.db")
    buat data = db_ambil_semua(db, "SELECT id, nama, email FROM pengguna")
    kembalikan sukses(data)
})
📌 Prinsip Utama: Selalu anggap input dari luar (request body, query param, header) sebagai data yang tidak bisa dipercaya. Validasi semua input sebelum memprosesnya.