Express.js 5
  • Bab 9 dari 9
  • 25 menit
  • 4 soal kuis

Produksi, Testing & Migrasi

Di bab ini (6 bagian)
  1. Tujuan
  2. Persiapan Keamanan di Lingkungan Produksi
  3. Performa dan Deployment
  4. Testing API dengan Supertest
  5. Panduan Migrasi dari Express 4 ke v5
  6. Rangkuman

Tujuan bab ini

Setelah mempelajari bab ini, kamu diharapkan mampu:

  • Menerapkan checklist keamanan produksi pada aplikasi Express.
  • Menguji API menggunakan library Supertest.
  • Memahami langkah-langkah migrasi dari Express versi 4 ke versi 5.

Persiapan Keamanan di Lingkungan Produksi

Saat mengembangkan aplikasi di komputer lokal, keamanan mungkin bukan prioritas utama. Namun, begitu aplikasi dirilis ke publik, API kamu akan menghadapi berbagai ancaman. Terdapat beberapa daftar periksa keamanan wajib yang perlu diterapkan.

Melindungi Header HTTP dengan Helmet

Secara bawaan, Express mengirimkan beberapa header HTTP yang bisa memberi petunjuk kepada penyerang tentang teknologi yang kamu gunakan. Misalnya, header x-powered-by secara otomatis diatur ke Express. Kamu harus menyembunyikan informasi ini. Library helmet membantu mengatur header keamanan HTTP seperti Content Security Policy (CSP) dan HTTP Strict Transport Security (HSTS) secara otomatis.

Membatasi Akses dengan CORS

Cross-Origin Resource Sharing (CORS) adalah mekanisme keamanan browser. Jika API kamu dipanggil dari domain yang berbeda, browser akan menolak permintaan tersebut kecuali API kamu mengizinkannya. Gunakan middleware cors() untuk membatasi origin mana saja yang boleh memanggil API kamu. Jangan gunakan origin terbuka bila tidak diperlukan.

Mencegah Serangan Brute Force

Untuk mencegah penyerang mencoba menebak kata sandi atau membebani server dengan ribuan permintaan per detik, gunakan express-rate-limit. Middleware ini mencegah brute force dan penyalahgunaan dengan membatasi jumlah request dari satu IP dalam rentang waktu tertentu.

Validasi Input dan Rahasia

Jangan pernah mempercayai data yang dikirim oleh klien. Gunakan skema validasi seperti zod atau joi untuk menolak data yang tidak valid sebelum diproses oleh pengontrol logika aplikasi. Selain itu, pastikan semua kredensial seperti password database atau kunci JWT disimpan di file .env dan jangan pernah melakukan commit file ini ke Git.

Berikut adalah contoh penerapan keamanan dasar dalam aplikasi Express:

import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import rateLimit from 'express-rate-limit';

const app = express();

// Menyembunyikan x-powered-by
app.disable('x-powered-by');

// Set header keamanan HTTP
app.use(helmet());

// Batasi origin yang boleh memanggil API
app.use(cors({
  origin: ['[https://app.id](https://app.id)'],
}));

// Batasi 100 request per 15 menit dari satu IP
app.use(rateLimit({
  windowMs: 15 * 60 * 1000,
  limit: 100,
}));

Performa dan Deployment

Sebelum aplikasi berjalan di server produksi, ada beberapa konfigurasi lingkungan dan arsitektur yang perlu diperhatikan.

Mode Produksi

Pastikan variabel lingkungan NODE_ENV diatur ke production. Hal ini memberi tahu Express untuk mengaktifkan cache untuk view engine (seperti EJS atau Pug) dan menyembunyikan detail pesan error agar tidak bocor ke pengguna.

Reverse Proxy dan Process Manager

Aplikasi Express sebaiknya tidak langsung menerima koneksi internet dari port 80 atau 443. Gunakan reverse proxy seperti Nginx atau Caddy di depan Express. Jika kamu melakukan ini, pastikan untuk menambahkan app.set('trust proxy', 1) agar Express mengenali alamat IP klien yang diteruskan oleh proxy.

Untuk menjaga aplikasi tetap hidup bila terjadi crash, gunakan process manager seperti PM2, systemd, atau jalankan di dalam kontainer Docker.

Graceful Shutdown

Saat server dihentikan (misalnya saat deploy versi baru), penting untuk tidak memutus koneksi yang sedang berjalan secara tiba-tiba. Terapkan graceful shutdown untuk menutup server dengan aman.

import mongoose from 'mongoose';

const server = app.listen(3000);

const shutdown = () => {
  console.log('Menutup server...');
  // Tutup server Express dari menerima koneksi baru
  server.close(async () => {
    // Putuskan koneksi database
    await mongoose.disconnect();
    process.exit(0);
  });
};

// Tangkap sinyal terminasi
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);

Testing API dengan Supertest

Pengujian otomatis memastikan aplikasi berfungsi sesuai kontrak dan tidak rusak saat kamu menambah fitur baru. supertest adalah library populer untuk menguji HTTP server di Node.js.

Supertest memanggil instance aplikasi Express kamu secara langsung tanpa perlu membuka port jaringan. Hal ini membuat pengujian berjalan cepat dan terisolasi. Kamu bisa menguji status code, isi respons (body), header, serta kasus error. Supertest dapat dipadukan dengan test runner apa pun, seperti node:test bawaan Node.js, Jest, atau Vitest.

import { test } from 'node:test';
import assert from 'node:assert/strict';
import request from 'supertest';
import app from '../src/app.js';

test('GET /api/health mengembalikan 200', async () => {
  const res = await request(app).get('/api/health');

  // Verifikasi status code
  assert.equal(res.status, 200);

  // Verifikasi isi respons
  assert.deepEqual(res.body, { status: 'ok' });
});

Panduan Migrasi dari Express 4 ke v5

Jika kamu memelihara proyek lama yang menggunakan Express 4, kamu disarankan untuk merencanakan migrasi ke Express 5. Ada beberapa perubahan penting yang memengaruhi cara aplikasi ditulis.

Penanganan Error Asinkron

Perubahan terbesar di Express 5 adalah dukungan penuh untuk Promise. Pada versi 4, error asinkron di dalam route handler harus ditangkap secara manual menggunakan blok try/catch dan diteruskan ke fungsi next(err). Di Express 5, setiap Promise yang reject atau error yang di-throw akan diteruskan otomatis ke error handler tanpa perlu blok penangkap manual.

Sintaks Routing

Express 5 memperbarui pustaka pencocokan path. Perhatikan perubahan sintaks berikut:

  • Wildcard: Pola /files/* di versi 4 kini harus ditulis secara eksplisit dengan nama seperti /files/*splat di versi 5.
  • Parameter Opsional: Sintaks /users/:id? tidak lagi didukung dan digantikan dengan kurung kurawal menjadi /users{/:id}.

Perubahan Metode dan Bawaan

Beberapa metode usang telah dihapus sepenuhnya:

  • app.del() dihapus, gunakan app.delete().
  • req.param() dihapus, baca nilai langsung dari req.params, req.query, atau req.body.
  • Penulisan respons usang seperti res.json(obj, 201) dihapus, gunakan format berantai res.status(201).json(obj).
  • res.sendfile() diubah kapitalisasinya menjadi res.sendFile().

Perhatikan juga konfigurasi middleware bawaan. Pada v5, pembacaan req.body tanpa parser akan mengembalikan undefined (sebelumnya objek kosong {}). Selain itu, opsi bawaan untuk express.urlencoded() kini memiliki nilai extended: false secara default (sebelumnya true). Properti req.query sekarang bersifat read-only (hanya-baca) dan tidak bisa ditulis ulang langsung.

Rangkuman

  • Terapkan middleware keamanan seperti helmet, cors, dan express-rate-limit sebelum merilis API ke produksi.
  • Atur NODE_ENV=production dan terapkan graceful shutdown agar server menutup koneksi database dan aplikasi dengan aman saat dimatikan.
  • Gunakan Supertest untuk menguji kontrak API secara cepat tanpa perlu membuka port server.
  • Express 5 secara otomatis menangani error asinkron (Promise yang gagal), menghilangkan keharusan menggunakan try/catch dan next(err) pada handler.
  • Perhatikan perubahan sintaks routing pada Express 5, seperti perubahan parameter opsional dari /:id? menjadi {/:id}.

Selesaikan bab ini

  1. 1. Baca materi
  2. 2. Kuis (4 soal)

Cek pemahaman

  1. 1Apa nilai bawaan (default) untuk opsi `extended` pada `express.urlencoded()` di Express versi 5?

  2. 2Supertest mewajibkan aplikasi Express untuk membuka port jaringan (seperti dengan `app.listen()`) agar dapat menjalankan pengujian HTTP.

  3. 3Manakah sintaks yang benar di Express 5 untuk mendefinisikan rute dengan parameter `id` yang bersifat opsional?

    // Rute Express 4: app.get('/users/:id?', ...)
    // Rute Express 5: app.get(..., ...)
  4. 4Bagaimana cara Express 5 menangani error yang terjadi pada operasi asinkron (misalnya Promise yang di-reject)?