Apa Itu API Rate Limiting dan Kenapa Penting
Pengenalan praktis tentang API rate limiting, kenapa penting untuk aplikasi web, dan bagaimana teknik ini membantu melindungi backend dari abuse, overload, dan lonjakan traffic.
Panduan praktis untuk membuat response API Express.js yang konsisten agar frontend React dan Next.js lebih mudah menangani success, error, validasi, dan data.
Bagikan artikel ini
Aplikasi frontend akan lebih mudah dibangun jika response API dari backend memiliki bentuk yang konsisten. Pada aplikasi React atau Next.js, response yang tidak konsisten sering menyebabkan defensive code berlebihan, error handling yang tidak jelas, dan runtime bug.
Artikel ini membahas cara menyusun response API Express.js untuk aplikasi frontend, termasuk success response, error response, validation error, pagination, dan response untuk file export.
Component frontend perlu tahu bentuk data yang akan diterima dari API. Jika satu endpoint mengembalikan data langsung, endpoint lain mengembalikan message, dan endpoint lain lagi mengembalikan string mentah, frontend harus menangani terlalu banyak kemungkinan.
Salah satu pendekatan yang mudah digunakan adalah memakai field utama yang sama untuk sebagian besar response API. Response sebaiknya menjelaskan apakah request berhasil, memiliki message yang jelas, dan optionally membawa data.
type ApiResponse<T> = {
status: "success" | "error";
message: string;
data?: T;
errors?: Record<string, string[]>;
};Struktur ini cukup sederhana untuk aplikasi kecil, tetapi masih fleksibel untuk validation error, payload data, dan pesan error umum.
Success response sebaiknya memiliki status yang jelas, message yang mudah dibaca, dan data yang dibutuhkan oleh frontend.
app.get("/api/projects", async (req, res) => {
const projects = await getProjects();
return res.status(200).json({
status: "success",
message: "Projects retrieved successfully.",
data: projects,
});
});Dengan bentuk response seperti ini, frontend bisa mengecek field status dan hanya membaca data ketika request berhasil.
Error response juga sebaiknya mengikuti bentuk yang sama. Hindari mengembalikan struktur yang benar-benar berbeda untuk setiap jenis error.
app.get("/api/projects/:id", async (req, res) => {
const project = await getProjectById(req.params.id);
if (!project) {
return res.status(404).json({
status: "error",
message: "Project not found.",
});
}
return res.status(200).json({
status: "success",
message: "Project retrieved successfully.",
data: project,
});
});Pattern ini membantu frontend menampilkan pesan error yang user-friendly tanpa perlu menebak bentuk response dari backend.
Validation error biasanya membutuhkan detail lebih banyak daripada error umum. Pattern yang cukup aman adalah tetap memakai status dan message, lalu menambahkan object errors untuk pesan per field.
return res.status(422).json({
status: "error",
message: "Validation failed.",
errors: {
email: ["Email is required."],
password: ["Password must be at least 8 characters."],
},
});Frontend bisa memakai message untuk alert umum dan object errors untuk menampilkan pesan pada masing-masing field form.
Untuk endpoint list, frontend sering membutuhkan metadata pagination. Agar tetap konsisten, simpan list data di dalam data dan informasi pagination di dalam field meta.
return res.status(200).json({
status: "success",
message: "Users retrieved successfully.",
data: users,
meta: {
page: 1,
limit: 10,
total: 100,
totalPages: 10,
},
});Dengan struktur ini, data list dan metadata pagination tetap terpisah sehingga component table atau list lebih mudah dirawat.
Untuk fitur export report, backend bisa saja mengembalikan URL file yang sudah dibuat, bukan raw data. Meski begitu, bentuk response sebaiknya tetap predictable.
return res.status(200).json({
status: "success",
message: "Report generated successfully.",
data: {
fileUrl: "/reports/sales-report-2026-06.pdf",
filename: "sales-report-2026-06.pdf",
},
});Ini membantu frontend agar tidak membuka file sebelum backend benar-benar selesai membuat report.
Daripada menulis object response secara manual di setiap controller, buat helper kecil agar format response tetap konsisten dan tidak banyak duplikasi.
function successResponse<T>(res: Response, message: string, data?: T) {
return res.status(200).json({
status: "success",
message,
data,
});
}
function errorResponse(res: Response, statusCode: number, message: string) {
return res.status(statusCode).json({
status: "error",
message,
});
}Helper seperti ini juga membuat perubahan lebih mudah. Jika format response perlu dikembangkan, perubahan bisa dilakukan di satu tempat.
Ketika response backend sudah predictable, API helper di frontend bisa dibuat lebih sederhana dan aman.
type ApiResponse<T> = {
status: "success" | "error";
message: string;
data?: T;
};
async function getProjects() {
const response = await fetch("/api/projects");
const result = (await response.json()) as ApiResponse<Project[]>;
if (result.status === "error") {
throw new Error(result.message);
}
return result.data ?? [];
}Pattern ini mengurangi risiko membaca property dari nilai undefined karena frontend memvalidasi status response dan memberi fallback ketika data tidak tersedia.
Response API yang konsisten membuat aplikasi frontend lebih mudah dibangun dan lebih mudah di-debug. Express.js tidak memaksa format response tertentu, jadi penting untuk menentukan standar sejak awal dan menggunakannya di semua controller.
Struktur sederhana dengan status, message, data, serta optional errors atau meta sudah cukup untuk banyak aplikasi full-stack. Kuncinya adalah konsistensi.
Jelajahi topik terkait
Pengenalan praktis tentang API rate limiting, kenapa penting untuk aplikasi web, dan bagaimana teknik ini membantu melindungi backend dari abuse, overload, dan lonjakan traffic.
Panduan praktis untuk mengatasi error undefined pada response API di Next.js dengan validasi response, normalisasi data, dan guard yang lebih aman di frontend.
Lanjut membaca
Baca artikel praktis tentang web development, API, realtime system, deployment, database, dan teknologi modern.