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

TypeScript & Docker

Di bab ini (6 bagian)
  1. Tujuan
  2. Memulai Express dengan TypeScript
  3. Handler Bertipe dan Memperluas Request
  4. Containerisasi dengan Docker
  5. Menjalankan Layanan dengan Docker Compose
  6. Rangkuman

Tujuan bab ini

Di bab ini, kamu akan belajar cara menggabungkan Express dengan TypeScript agar kodemu lebih aman dan minim bug. Kamu juga akan mempelajari cara membungkus aplikasi menggunakan Dockerfile multi-stage dan menjalankannya bersama database menggunakan Docker Compose.

Memulai Express dengan TypeScript

Menambahkan TypeScript ke dalam proyek Express memberikan keuntungan besar berupa pengecekan tipe statis dan autocompletion yang lebih baik. Langkah pertama adalah menginstal dependensi yang dibutuhkan.

Kamu perlu menginstal Express, lalu menambahkan TypeScript dan tipe deklarasinya sebagai dependensi pengembangan (devDependencies). Paket tsx sangat berguna untuk menjalankan TypeScript langsung tanpa proses kompilasi manual saat masa pengembangan.

npm i express
npm i -D typescript tsx @types/node @types/express
npx tsc --init

Setelah itu, kamu perlu mengatur tsconfig.json. Pastikan kamu menggunakan target ECMAScript yang modern seperti ES2023 dan mengatur modul ke NodeNext. Ini sangat penting agar TypeScript memahami modul ES yang digunakan di Node.js terbaru.

Skrip package.json

Untuk memudahkan pengembangan, tambahkan skrip berikut di package.json:

"scripts": {
  "dev": "tsx watch src/server.ts",
  "build": "tsc",
  "start": "node dist/server.js"
}

Handler Bertipe dan Memperluas Request

TypeScript bersinar ketika kamu mulai mendefinisikan bentuk data yang masuk ke dalam API. Express menyediakan antarmuka generik Request yang menerima empat parameter tipe: Params, ResBody, ReqBody, dan Query.

Sebagai contoh, bila kamu ingin membuat endpoint untuk menambahkan buku, kamu bisa mendefinisikan bentuk body request terlebih dahulu:

import express, { type Request, type Response } from 'express';

interface CreateBookBody {
  title: string;
  author: string;
  year?: number;
}

const app = express();
app.use(express.json());

app.post('/api/books', (req: Request<{}, {}, CreateBookBody>, res: Response) => {
  const { title, author } = req.body; // Properti ini kini ter-type dengan aman
  res.status(201).json({ title, author });
});

Menambahkan Tipe Kustom pada Request

Seringkali, middleware menambahkan properti baru ke objek request, misalnya req.user setelah proses autentikasi berhasil. Agar TypeScript tidak mengeluh bahwa properti tersebut tidak ada, kamu harus memperluas tipe global dari Express.

Buat file deklarasi seperti src/types/express.d.ts:

declare global {
  namespace Express {
    interface Request {
      user?: { sub: number; role: string };
    }
  }
}
export {};

Dengan deklarasi ini, TypeScript akan mengenali properti user di seluruh aplikasi.

Containerisasi dengan Docker

Setelah kodemu siap, saatnya membungkusnya agar bisa berjalan konsisten di server mana pun. Docker adalah standar industri untuk hal ini.

Dockerfile Multi-stage

Untuk mendapatkan ukuran image yang kecil dan efisien, gunakan metode multi-stage build. Tahap pertama digunakan untuk mengunduh seluruh dependensi, sedangkan tahap kedua hanya mengambil dependensi produksi dan kode aplikasi.

# Tahap 1: dependensi produksi
FROM node:24-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev

# Tahap 2: image akhir
FROM node:24-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]

Pastikan kamu menggunakan USER node alih-alih root untuk alasan keamanan. Jangan lupa menyiapkan file .dockerignore berisi node_modules, .env, .git, dan folder uploads agar file tidak penting tidak masuk ke dalam image.

Docker Diagram

Menjalankan Layanan dengan Docker Compose

Dalam dunia nyata, API Express biasanya tidak berjalan sendirian. Ia butuh layanan lain seperti database. Docker Compose memudahkan kita menjalankan banyak container sekaligus.

Buat file compose.yaml:

services:
  api:
    build: .
    ports: ["3000:3000"]
    env_file: .env
    depends_on: [db]
  db:
    image: postgres:17
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Satu hal yang perlu diingat, di dalam jaringan Docker Compose, host dari database adalah nama servisnya itu sendiri. Jadi, URL koneksimu akan menjadi postgresql://app:secret@db:5432/appdb.

Jalankan perintah docker compose up -d --build di terminal untuk membangun image dan menjalankan semua layanan di latar belakang.

Rangkuman

  • TypeScript menambahkan pengecekan statis pada Express, membutuhkan paket @types/express dan compiler seperti tsx atau tsc.
  • Gunakan parameter generik pada objek Request (misalnya Request<{}, {}, TBody>) untuk memberi tipe data pada body atau parameter.
  • Perluas namespace global Express jika ingin menambahkan properti kustom pada objek request seperti req.user.
  • Dockerfile multi-stage memisahkan tahap instalasi dependensi dari image akhir untuk ukuran yang lebih ringan dan aman.
  • Jalankan container Node.js sebagai non-root (misal USER node) dan hindari memasukkan file .env ke dalam image.
  • Docker Compose mengatur layanan API dan database secara bersamaan, di mana komunikasi antar-container menggunakan nama servis sebagai alamat host.

Selesaikan bab ini

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

Cek pemahaman

  1. 1Bagaimana cara memberikan tipe pada request body di handler Express menggunakan TypeScript?

  2. 2Kamu harus menyalin file `.env` ke dalam image Docker agar aplikasi berjalan dengan lancar saat di-deploy.

  3. 3Apa peran `USER node` dalam penulisan Dockerfile untuk Node.js?