Validasi Request API Menggunakan Zod di Express.js: Panduan Lengkap untuk Developer

Membangun API yang kokoh dan dapat diandalkan adalah fondasi utama dalam pengembangan aplikasi modern. Salah satu aspek krusial yang sering terabaikan, atau setidaknya tidak dilakukan dengan optimal, adalah validasi data. Bayangkan Anda menerima data dari frontend atau klien lain tanpa proses verifikasi; ini sama saja dengan membuka pintu lebar-lebar bagi bug, celah keamanan, dan inkonsistensi data. Di sinilah validasi request API berperan penting.

Dalam ekosistem Node.js, Express.js adalah framework yang sangat populer untuk membangun API. Sementara Express.js memberikan kebebasan dan fleksibilitas, ia tidak datang dengan solusi validasi bawaan yang komprehensif. Inilah mengapa kita memerlukan bantuan pihak ketiga. Kali ini, kita akan fokus pada Zod, sebuah schema declaration and validation library yang dirancang dengan filosofi TypeScript-first. Zod menawarkan pengalaman developer yang luar biasa, keamanan tipe, dan validasi runtime yang tangguh, menjadikannya pilihan ideal untuk API Express.js.

Artikel ini akan memandu Anda secara langkah demi langkah tentang cara mengimplementasikan validasi request API menggunakan Zod di Express.js. Kita akan membahas dari setup dasar hingga skenario yang lebih kompleks, serta berbagi insight praktis dari pengalaman nyata di lapangan.

Daftar Isi sembunyikan

Mengapa Validasi API Itu Krusial?

Sebelum menyelam ke implementasi, mari kita pahami mengapa validasi data masuk adalah prioritas utama dalam pengembangan API:

  • Keamanan: Data yang tidak divalidasi adalah pintu masuk potensial untuk serangan seperti SQL Injection, Cross-Site Scripting (XSS), atau serangan injeksi lainnya. Memastikan bahwa data yang diterima sesuai dengan ekspektasi mengurangi risiko ini secara signifikan.
  • Integritas Data: Validasi membantu menjaga konsistensi dan kebenaran data di database Anda. Anda tidak ingin menyimpan data yang tidak lengkap, salah format, atau di luar jangkauan yang diizinkan.
  • Pengalaman Developer (DX): Dengan validasi yang jelas, frontend developer atau konsumer API lainnya dapat memahami format data yang diharapkan dan menerima pesan error yang informatif jika ada kesalahan. Ini mempercepat proses pengembangan dan debugging.
  • Pencegahan Bug: Banyak bug terjadi karena asumsi yang salah tentang format data. Validasi di sisi server memastikan bahwa aplikasi Anda hanya beroperasi dengan data yang valid, mengurangi kemungkinan crash atau perilaku tidak terduga.
  • Kepatuhan Bisnis: Dalam banyak kasus, ada aturan bisnis yang harus dipatuhi terkait format dan nilai data. Validasi API adalah tempat pertama untuk menegakkan aturan-aturan ini.

Mengenal Zod: Schema Validation yang Modern dan TypeScript-First

Zod adalah sebuah library validasi schema yang relatif baru namun cepat populer. Dibuat oleh Colin McDonnell (juga pencipta react-hook-form), Zod menonjol karena beberapa alasan:

  • TypeScript-First: Ini adalah kekuatan terbesar Zod. Anda mendefinisikan schema dengan Zod, dan secara otomatis Zod akan menginfer tipe TypeScript yang sesuai. Ini berarti Anda hanya menulis definisi data sekali, dan mendapatkan validasi runtime sekaligus tipe statis.
  • Validasi Runtime yang Kuat: Zod menyediakan serangkaian validator bawaan yang ekstensif untuk berbagai tipe data (string, number, boolean, array, object, date, dll.) dan juga memungkinkan validasi kustom yang kompleks.
  • Immutability: Semua operasi Zod mengembalikan schema baru, menjadikannya sangat chainable dan mudah digunakan.
  • Mudah Digunakan: Sintaksnya bersih, intuitif, dan mudah dibaca, bahkan untuk validasi yang kompleks.
  • Performa: Zod dirancang agar efisien, dengan performa yang sangat kompetitif dibandingkan library validasi lainnya.

Dengan Zod, Anda dapat merasa yakin bahwa data yang masuk ke aplikasi Anda tidak hanya valid secara sintaksis, tetapi juga sesuai dengan struktur dan tipe yang Anda harapkan.

Persiapan Awal: Setup Proyek Express.js

Mari kita mulai dengan menyiapkan proyek Express.js dasar. Jika Anda sudah memiliki proyek, Anda bisa langsung ke bagian berikutnya.

1. Inisialisasi Proyek

Buka terminal Anda dan buat folder proyek baru, lalu inisialisasi Node.js:

mkdir express-zod-api
cd express-zod-api
npm init -y

2. Instalasi Dependensi

Kita akan menginstal Express.js dan Zod. Jika Anda menggunakan TypeScript, instal juga tipe definisinya.

npm install express zod
npm install --save-dev typescript @types/express @types/node ts-node-dev

Jika Anda menggunakan TypeScript, buat file tsconfig.json:

npx tsc --init

Kemudian, atur "rootDir": "./src" dan "outDir": "./dist" di tsconfig.json.

3. Struktur Dasar Aplikasi

Buat folder src dan di dalamnya file app.ts (untuk TypeScript) atau app.js (untuk JavaScript). Ini adalah struktur awal aplikasi Express kita:

Contoh src/app.ts:

import express from 'express';
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json()); // Middleware untuk parsing JSON body
app.get('/', (req, res) => {
res.send('API is running!');
});
app.listen(PORT, () => {
console.log(`Server berjalan di http://localhost:${PORT}`);
});

Tambahkan script untuk menjalankan aplikasi di package.json:

"scripts": {
"start": "ts-node-dev --respawn --transpile-only src/app.ts"
}

Sekarang, Anda bisa menjalankan aplikasi dengan npm start.

Langkah 1: Mendefinisikan Schema dengan Zod

Inti dari validasi dengan Zod adalah mendefinisikan schema yang menggambarkan struktur data yang Anda harapkan. Mari kita buat beberapa schema contoh.

Buat folder src/schemas dan di dalamnya file userSchema.ts (atau userSchema.js).

Contoh src/schemas/userSchema.ts:

import { z } from 'zod';
export const createUserSchema = z.object({
name: z.string().min(3, 'Nama harus memiliki minimal 3 karakter').max(50, 'Nama tidak boleh lebih dari 50 karakter'),
email: z.string().email('Format email tidak valid'),
age: z.number().int('Umur harus berupa bilangan bulat').positive('Umur harus angka positif').optional(), // Optional field
password: z.string().min(6, 'Password harus minimal 6 karakter'),
});
export const updateUserSchema = z.object({
name: z.string().min(3).max(50).optional(),
email: z.string().email().optional(),
age: z.number().int().positive().optional(),
});
export const userIdSchema = z.object({
id: z.string().uuid('ID pengguna harus format UUID yang valid'),
});
export const queryFilterSchema = z.object({
limit: z.string().optional().transform(val => val ? Number(val) : 10), // Transform string to number
offset: z.string().optional().transform(val => val ? Number(val) : 0),
search: z.string().optional(),
});

Penjelasan:

  • z.object({}): Mendefinisikan schema untuk objek.
  • z.string(), z.number(): Tipe data dasar.
  • .min(), .max(), .email(), .uuid(), .int(), .positive(): Validator bawaan Zod untuk menambahkan batasan.
  • .optional(): Menandai bidang sebagai opsional.
  • .transform(): Mengubah nilai setelah validasi berhasil (misalnya dari string menjadi number untuk query params).

Langkah 2: Membuat Middleware Validasi Kustom

Untuk mengintegrasikan Zod dengan Express.js, kita akan membuat fungsi middleware yang bertanggung jawab untuk melakukan validasi. Middleware ini akan mengambil schema Zod, memvalidasi data request (req.body, req.query, atau req.params), dan melanjutkan request jika valid, atau mengirim respons error jika tidak.

Buat folder src/middlewares dan di dalamnya file validateRequest.ts.

Contoh src/middlewares/validateRequest.ts:

import { Request, Response, NextFunction } from 'express';
import { AnyZodObject, ZodError } from 'zod';
const validate = (schema: AnyZodObject) =>
(req: Request, res: Response, next: NextFunction) => {
try {
schema.parse({
body: req.body,
query: req.query,
params: req.params,
});
next();
} catch (error: any) {
if (error instanceof ZodError) {
return res.status(400).json({
status: 'error',
message: 'Data tidak valid',
errors: error.errors.map(err => ({
path: err.path.join('.'),
message: err.message,
})),
});
}
return res.status(500).json({
status: 'error',
message: 'Terjadi kesalahan server internal',
});
}
};
export default validate;

Penjelasan Middleware:

  • Fungsi validate menerima satu argumen: schema Zod. Ini adalah sebuah higher-order function yang mengembalikan middleware Express.
  • Di dalam middleware, kita mencoba memanggil schema.parse() dengan objek yang berisi req.body, req.query, dan req.params. Ini memungkinkan kita membuat satu schema yang memvalidasi beberapa bagian dari request.
  • Jika validasi berhasil, next() dipanggil untuk melanjutkan ke handler route berikutnya.
  • Jika terjadi error (biasanya ZodError), kita menangkapnya, mengembalikan status 400 (Bad Request), dan menyertakan detail error dari Zod dalam respons JSON. Ini sangat membantu frontend developer.

Penting: Untuk schema di userSchema.ts, kita mendefinisikan schema untuk bagian-bagian spesifik (misalnya hanya body atau hanya params). Namun, middleware validate mengharapkan schema yang mencakup objek { body, query, params }. Jadi, saat menggunakan middleware ini, Anda perlu membuat schema gabungan atau schema spesifik yang membungkus bagian request yang ingin divalidasi. Contoh, jika Anda ingin memvalidasi hanya req.body, Anda akan membuat schema seperti z.object({ body: createUserSchema }).

Langkah 3: Mengintegrasikan Middleware ke Route Express.js

Sekarang, mari kita gunakan middleware validasi kita di beberapa route Express.js.

Update file src/app.ts:

import express from 'express';
import { z } from 'zod';
import validate from './middlewares/validateRequest';
import { createUserSchema, updateUserSchema, userIdSchema, queryFilterSchema } from './schemas/userSchema';
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
app.get('/', (req, res) => {
res.send('API is running!');
});
// Route untuk membuat pengguna baru
app.post('/users', validate(z.object({ body: createUserSchema })), (req, res) => {
const newUser = req.body;
console.log('User created:', newUser);
res.status(201).json({ message: 'Pengguna berhasil dibuat', data: newUser });
});
// Route untuk mendapatkan daftar pengguna dengan filter
app.get('/users', validate(z.object({ query: queryFilterSchema })), (req, res) => {
const filters = req.query; // Sudah divalidasi dan ditransformasi oleh Zod
console.log('Fetching users with filters:', filters);
res.status(200).json({ message: 'Daftar pengguna', filters });
});
// Route untuk mendapatkan detail pengguna berdasarkan ID
app.get('/users/:id', validate(z.object({ params: userIdSchema })), (req, res) => {
const userId = req.params.id; // Sudah divalidasi oleh Zod
console.log('Fetching user with ID:', userId);
res.status(200).json({ message: `Detail pengguna ${userId}` });
});
// Route untuk mengupdate pengguna
app.put('/users/:id', validate(z.object({ params: userIdSchema, body: updateUserSchema })), (req, res) => {
const userId = req.params.id;
const updatedData = req.body;
console.log(`Updating user ${userId} with data:`, updatedData);
res.status(200).json({ message: `Pengguna ${userId} berhasil diperbarui`, data: updatedData });
});
app.listen(PORT, () => {
console.log(`Server berjalan di http://localhost:${PORT}`);
});

Sekarang, ketika Anda mengirim request ke endpoint ini, middleware validate akan bekerja terlebih dahulu. Jika data tidak valid, Anda akan menerima respons error 400 dengan detail kesalahan. Jika valid, request akan diteruskan ke handler route.

Menangani Error Validasi Secara Global

Pendekatan di atas sudah cukup baik, namun jika Anda memiliki banyak route dan ingin penanganan error yang lebih terpusat untuk semua error di aplikasi, Anda bisa membuat middleware error-handling global Express.js.

Update src/app.ts untuk menambahkan middleware error di akhir semua route:

// ... kode app.ts sebelumnya ...
// Middleware penanganan error global
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
if (err instanceof ZodError) {
return res.status(400).json({
status: 'error',
message: 'Data tidak valid',
errors: err.errors.map(zodErr => ({
path: zodErr.path.join('.'),
message: zodErr.message,
})),
});
}
console.error(err); // Log error untuk debugging
return res.status(500).json({
status: 'error',
message: 'Terjadi kesalahan server internal',
});
});
app.listen(PORT, () => {
console.log(`Server berjalan di http://localhost:${PORT}`);
});

Dengan middleware error global ini, Anda bisa menyederhanakan middleware validateRequest.ts dengan hanya melemparkan error ZodError, dan middleware global akan menangkapnya. Namun, untuk validasi, pendekatan langsung di middleware route seperti sebelumnya seringkali lebih eksplisit dan mudah dilacak. Pilihan ada di tangan Anda, tergantung skala dan kompleksitas aplikasi.

Fitur Lanjutan Zod untuk Validasi yang Lebih Canggih

Zod menawarkan banyak fitur canggih untuk menangani skenario validasi yang lebih kompleks.

1. Custom Error Messages

Anda sudah melihat ini di contoh createUserSchema. Anda bisa memberikan pesan error kustom untuk setiap validator:

name: z.string().min(3, { message: 'Nama minimal 3 karakter' })

2. `refine()` dan `superRefine()` untuk Validasi Kompleks

Ketika validator bawaan tidak cukup, refine() dan superRefine() memungkinkan Anda menambahkan logika validasi kustom.

refine() digunakan untuk validasi yang hanya bergantung pada satu bidang dan mengembalikan boolean:

password: z.string().min(6).refine(password => /[A-Z]/.test(password), 'Password harus mengandung setidaknya satu huruf kapital'),

superRefine() digunakan untuk validasi yang lebih kompleks, bisa melibatkan beberapa bidang, dan memberikan kontrol penuh atas pesan error:

const registrationSchema = z.object({
email: z.string().email(),
password: z.string().min(6),
confirmPassword: z.string().min(6),
}).superRefine(({ password, confirmPassword }, ctx) => {
if (password !== confirmPassword) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Password dan konfirmasi password tidak cocok',
path: ['confirmPassword'],
});
}
});

3. Transformasi Data (`transform()`)

Kita sudah melihat transform() untuk mengubah string menjadi number. Fitur ini sangat berguna untuk membersihkan atau memformat data setelah validasi:

price: z.string().transform(val => parseFloat(val)),
tags: z.string().transform(val => val.split(',').map(tag => tag.trim())),

4. Validasi `union` dan `intersection`

Zod juga mendukung tipe data kompleks seperti union (salah satu dari beberapa schema) dan intersection (gabungan dari beberapa schema).

const stringOrNumber = z.union([z.string(), z.number()]);
const adminUserSchema = createUserSchema.and(z.object({
role: z.literal('admin'),
}));

Pengalaman dan Pertimbangan Praktis

Dari pengalaman saya mengimplementasikan Zod di berbagai proyek Express.js, ada beberapa poin penting yang perlu dipertimbangkan:

Integrasi dengan TypeScript: Manfaat Type Inference

Ini adalah alasan utama mengapa saya sangat merekomendasikan Zod, terutama di proyek TypeScript. Ketika Anda mendefinisikan schema seperti createUserSchema, Anda bisa dengan mudah menginfer tipe TypeScript-nya:

type CreateUserInput = z.infer<typeof createUserSchema>;

Dengan begini, Anda mendapatkan validasi runtime dan tipe yang aman di seluruh kode Anda, mulai dari middleware, controller, hingga layanan database. Ini secara drastis mengurangi kesalahan ketik dan bug yang berhubungan dengan tipe data, sekaligus meningkatkan produktivitas karena IDE Anda akan memberikan autocomplete yang akurat.

Kapan Menggunakan Validasi di Client-Side vs Server-Side

Seringkali muncul pertanyaan, “Apakah saya perlu validasi di frontend jika sudah ada di backend?” Jawabannya adalah YA. Validasi frontend meningkatkan pengalaman pengguna dengan memberikan umpan balik instan, mengurangi jumlah request yang gagal ke server. Namun, validasi server-side adalah lapisan keamanan dan integritas data yang tidak boleh dilewatkan. Ingat, validasi frontend bisa di-bypass oleh pengguna yang berniat jahat, sehingga backend harus selalu menjadi sumber kebenaran terakhir.

Dampak Performa: Zod Relatif Cepat

Saya pernah khawatir apakah menambahkan lapisan validasi akan memperlambat API. Namun, dalam pengujian dan penggunaan di produksi, Zod terbukti sangat cepat. Untuk API dengan traffic tinggi, dampak performa dari Zod biasanya dapat diabaikan dibandingkan dengan I/O database atau logika bisnis yang lebih kompleks. Fokuslah pada penulisan schema yang efisien dan hindari validasi yang terlalu rumit jika tidak benar-benar diperlukan.

Strategi untuk Schema Kompleks dan Reusable

Pada proyek skala besar, Anda mungkin akan memiliki banyak schema yang saling terkait atau memiliki bagian yang sama. Strategi yang baik adalah memecah schema menjadi bagian-bagian yang lebih kecil dan dapat digunakan kembali:

const baseUserInfo = z.object({
name: z.string().min(3),
email: z.string().email(),
});
const fullUserSchema = baseUserInfo.extend({
password: z.string().min(6),
role: z.enum(['user', 'admin']),
});

Menggunakan .extend(), .merge(), atau bahkan sekadar menyusun objek Zod membantu menjaga kode tetap bersih dan mudah dikelola.

Trade-off: Mempelajari Zod vs Framework Validator Lain

Mempelajari library baru selalu membutuhkan waktu. Zod memiliki kurva pembelajaran yang relatif landai, terutama jika Anda sudah familiar dengan konsep validasi schema. Dibandingkan dengan library lain seperti Joi atau Yup, sintaks Zod terasa lebih modern dan berorientasi pada TypeScript. Jika proyek Anda menggunakan TypeScript, Zod adalah investasi waktu yang sangat berharga karena manfaat type safety yang ditawarkannya.

Masalah yang Sering Terjadi Saat Validasi dengan Zod di Express.js

Dalam praktiknya, ada beberapa masalah umum yang sering dihadapi developer saat mengimplementasikan Zod dengan Express.js:

1. Schema Tidak Sesuai dengan Data yang Dikirim

Gejala: Pesan error “Required” atau “Invalid type” muncul padahal data sudah dikirim, atau sebaliknya, data yang seharusnya tidak valid malah lolos validasi.

Penyebab: Seringkali terjadi karena kesalahan penamaan bidang di schema Zod atau di request body. Misalnya, di schema Anda menulis userName tapi di request mengirim user_name, atau Anda mengharapkan number tapi mengirim string. Kesalahan umum lainnya adalah lupa bahwa req.query dan req.params selalu berupa string, sehingga perlu ditransformasi menjadi number jika diharapkan sebagai angka.

Solusi: Periksa kembali dengan teliti payload request Anda dan bandingkan dengan definisi schema Zod. Pastikan nama bidang, tipe data, dan validator (misalnya .optional() atau .nullable()) sudah sesuai. Untuk req.query dan req.params, gunakan .transform(Number) atau z.coerce.number().

2. Middleware Tidak Dipanggil di Route yang Benar

Gejala: Validasi tidak terjadi sama sekali, atau request selalu lolos meskipun data salah.

Penyebab: Lupa menambahkan middleware validate(schema) ke route handler, atau menempatkannya setelah handler utama (middleware harus berada sebelum handler yang memproses data). Bisa juga karena ada route lain yang cocok dan dieksekusi terlebih dahulu tanpa validasi.

Solusi: Pastikan middleware validasi Anda diletakkan sebelum fungsi handler utama di route Express.js. Periksa urutan route; jika ada route yang lebih generik tanpa validasi, itu mungkin akan cocok terlebih dahulu.

3. Penanganan Error yang Tidak Konsisten

Gejala: Kadang respons error 400 berisi detail, kadang hanya 500, kadang formatnya berbeda-beda.

Penyebab: Tidak ada standar respons error, atau middleware error-handling tidak menangani semua jenis error dengan cara yang sama. Mungkin ada middleware atau try-catch lain yang menimpa atau mengabaikan ZodError.

Solusi: Pastikan Anda memiliki satu titik sentral untuk menangani ZodError, baik itu di dalam middleware validasi itu sendiri atau di middleware error-handling global Express.js. Konsistenkan format respons error agar frontend developer mudah mengonsumsinya.

4. Lupa Menginstal `@types/express` atau `@types/node`

Gejala: Kesalahan tipe di TypeScript saat menggunakan Request, Response, NextFunction, atau variabel global Node.js.

Penyebab: Anda menggunakan TypeScript tetapi belum menginstal definisi tipe untuk Express.js atau Node.js.

Solusi: Instal dependensi tipe yang diperlukan: npm install --save-dev @types/express @types/node.

5. Zod Schema Terlalu Kompleks, Sulit Dibaca

Gejala: Schema Zod menjadi sangat panjang, bercabang, dan sulit dipahami atau di-debug.

Penyebab: Kurangnya modularisasi schema, atau mencoba melakukan terlalu banyak validasi kompleks dalam satu schema besar.

Solusi: Pecah schema menjadi bagian-bagian yang lebih kecil dan dapat digunakan kembali (seperti yang dibahas di “Strategi untuk Schema Kompleks”). Gunakan .extend() atau .merge() untuk menggabungkan schema-schema kecil menjadi yang lebih besar. Komentari bagian-bagian kompleks untuk meningkatkan keterbacaan.

FAQ

Apakah Zod lebih baik dari Joi atau Yup?

Masing-masing library memiliki kelebihan. Zod sangat menonjol jika Anda menggunakan TypeScript karena integrasi type inference-nya yang superior. Joi dan Yup juga merupakan pilihan yang solid, terutama di proyek JavaScript murni. Zod terasa lebih modern dan memiliki sintaks yang lebih ringkas bagi banyak developer.

Bisakah Zod digunakan untuk validasi form di frontend?

Ya, tentu saja! Zod sangat populer digunakan di frontend, terutama dengan framework seperti React dan library form seperti React Hook Form atau Formik. Dengan Zod, Anda bisa menggunakan schema yang sama persis di frontend dan backend, memastikan konsistensi validasi di kedua sisi.

Bagaimana Zod menangani nilai null/undefined?

Secara default, Zod menganggap bidang sebagai required dan tidak boleh null atau undefined. Jika Anda ingin memperbolehkan nilai null, gunakan z.string().nullable(). Jika ingin memperbolehkan undefined (membuat bidang opsional), gunakan z.string().optional(). Anda juga bisa menggunakan z.string().nullish() untuk memperbolehkan keduanya.

Apakah perlu validasi di frontend dan backend?

Sangat dianjurkan. Validasi frontend memberikan umpan balik instan kepada pengguna dan meningkatkan UX. Validasi backend adalah lapisan keamanan mutlak yang melindungi integritas data Anda dari segala bentuk input yang tidak valid, baik disengaja maupun tidak disengaja. Jangan pernah hanya mengandalkan validasi frontend.

Bisakah Zod melakukan sanitasi data?

Zod bukan library sanitasi data utama, tetapi fitur .transform()-nya dapat digunakan untuk beberapa sanitasi sederhana, seperti .trim() pada string, mengubah tipe data, atau memformat input. Untuk sanitasi yang lebih kompleks (misalnya menghilangkan tag HTML atau karakter berbahaya), Anda mungkin perlu mengombinasikannya dengan library sanitasi khusus.

Kesimpulan

Mengimplementasikan validasi request API menggunakan Zod di Express.js adalah langkah cerdas untuk membangun API yang lebih aman, kuat, dan mudah dikelola. Dengan pendekatan TypeScript-first Zod, Anda tidak hanya mendapatkan validasi runtime yang tangguh, tetapi juga keamanan tipe yang tak ternilai harganya di seluruh basis kode Anda.

Dari mendefinisikan schema yang bersih hingga membuat middleware validasi yang efisien, proses ini adalah praktik terbaik yang akan menghemat waktu debugging Anda di masa depan dan meningkatkan kepercayaan pada API Anda. Ingatlah untuk selalu memvalidasi input di sisi server, karena ini adalah benteng pertahanan terakhir Anda. Dengan Zod, proses ini menjadi lebih menyenangkan dan produktif.

TAGS: Zod, Express.js, Node.js, API Validation, JavaScript, TypeScript, Web Development, Backend, Software Engineering, Developer Tools


Baca Juga

You May Also Like

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *