Testing dan Dokumentasi API dengan Postman dan Swagger

Tutorial
Jul 18, 2026
19 mnt baca
14 tayangan
Testing dan Dokumentasi API dengan Postman dan Swagger
A

Admin

Penulis Artikel

Testing dan Dokumentasi API dengan Postman dan Swagger untuk Pemula

Di artikel sebelumnya, kita sudah punya automated test dengan Jest dan Supertest yang memastikan logika API kita benar. Tapi automated test itu ditulis oleh developer, untuk developer. Ketika tim frontend, QA manual, atau partner eksternal ingin mencoba API secara interaktif atau membaca dokumentasi endpoint yang tersedia, mereka butuh alat yang berbeda.

Artikel ini akan membahas cara menyusun collection testing manual di Postman dan cara mendokumentasikan API dengan Swagger (OpenAPI) — keduanya dipraktikkan langsung di Todo List API yang sudah kita bangun.

Artikel ini melanjutkan project Todo List API dari artikel pertama hingga ketujuh. Setelah ini, kita akan menutup seri dengan Struktur Project Node.js Siap Produksi.


1. Postman: Menguji API Secara Interaktif

Analogi: kalau curl di artikel-artikel sebelumnya itu seperti menulis surat resmi satu-satu setiap kali mau bertanya ke petugas TU, Postman itu seperti buku formulir siap pakai yang sudah ada kolom-kolom kosong tinggal isi — kamu tidak perlu menulis ulang format suratnya dari nol setiap kali, tinggal ganti isian yang berubah (misalnya ID todo atau token login), lalu kirim.

1.1 Membuat Collection dan Environment

Di Postman, buat sebuah Collection baru bernama Todo List API. Di dalam collection ini kita akan mengelompokkan request berdasarkan resource: Auth, Todos, dan Stats.

Lalu buat Environment baru bernama Todo API - Local dengan variabel berikut:

Variable

Initial Value

Keterangan

baseUrl

http://localhost:3000

Alamat dasar server

token

(kosongkan)

Akan diisi otomatis setelah login

apiKey

isi dengan nilai EXTERNAL_API_KEY di .env

Untuk endpoint stats

Tampilan awal postman

Penjelasan teknis: variabel environment ini nantinya dipanggil di URL atau header dengan format {{namaVariabel}}, misalnya {{baseUrl}}/api/todos. Postman akan otomatis mengganti placeholder itu dengan nilai sesungguhnya saat request dikirim. Ini persis konsep yang sama dengan process.env.MONGODB_URI yang kita pakai sejak artikel kedua — bedanya di sini yang “menyuntikkan” nilainya adalah Postman, bukan Node.js. Keuntungannya: kalau nanti API di-deploy ke server lain, kamu cukup ganti nilai baseUrl di satu tempat, tanpa perlu edit satu-satu di puluhan request.

1.2 Request Login dengan Auto-Save Token

Buat request POST ke {{baseUrl}}/api/auth/login dengan body:

{
  "email": "budi@example.com",
  "password": "rahasia123"
}

Di tab Scripts -> Post-response pada request ini, tambahkan script berikut:

pm.test("Status code is 200", function () {
  pm.response.to.have.status(200);
});

pm.test("Response has token", function () {
  const jsonData = pm.response.json();
  pm.expect(jsonData.data.token).to.exist;

  // Simpan token ke environment variable secara otomatis
  pm.environment.set("token", jsonData.data.token);
});
enviroment postman

kemudian pergi ke tab body dan masukkan data baru dalam format json seperti dibawah. Ketika klik Send akan memunculkan response yang berisi token yang otomatis akan tesimpan pada enviromen "token" yang dibuat sebelumnya.

testing req login

Penjelasan teknis:

  • pm.test(nama, function) adalah cara Postman mendefinisikan satu kasus pengujian — konsepnya mirip persis dengan it("...", () => {...}) yang kita tulis di Jest pada artikel sebelumnya, hanya beda platform.

  • pm.response.json() mengambil body response dan mem-parsingnya jadi object JavaScript, sama seperti res.body yang kita pakai di Supertest.

  • Baris paling penting adalah pm.environment.set("token", jsonData.data.token) — ini yang membuat token hasil login otomatis tersimpan ke variabel {{token}}, sehingga request-request lain (create todo, get todo, dll) tidak perlu copy-paste token secara manual setiap kali login ulang.

1.3 Request yang Membutuhkan Autentikasi

Buat request POST ke {{baseUrl}}/api/todos. Di tab Authorization, pilih tipe Bearer Token, lalu isi dengan {{token}}. cotohnya misal ingin menguji endpoint Get Todo:

Test inout token

Penjelasan teknis: ini secara otomatis menambahkan header Authorization: Bearer <isi_token> ke request — persis format yang dibaca oleh middleware protect yang kita buat di artikel autentikasi JWT:

// Cuplikan dari src/middlewares/auth.middleware.js (artikel #4)
if (req.headers.authorization && req.headers.authorization.startsWith("Bearer")) {
  token = req.headers.authorization.split(" ")[1];
}

Karena token diisi lewat variabel {{token}} yang otomatis ter-update setelah login (poin 1.2), kamu tidak perlu login ulang dan copy-paste manual setiap kali mau menguji endpoint todo.

1.4 Menyusun Folder dan Urutan Testing

Susun collection dengan struktur folder berikut, meniru urutan alur pemakaian API sesungguhnya:

Todo List API/
├── Auth/
│   ├── Register
│   └── Login
├── Todos/
│   ├── Create Todo
│   ├── Get All Todos
│   ├── Get Todo By ID
│   ├── Update Todo
│   └── Delete Todo
└── Stats/
    └── Get Summary
Struktur postman

Penjelasan teknis: urutan folder ini bukan cuma soal rapi-rapian — Postman punya fitur Collection Runner yang menjalankan semua request dalam collection secara berurutan dari atas ke bawah. Karena Login diletakkan sebelum Todos, variabel {{token}} sudah terisi duluan sebelum request-request di folder Todos dijalankan. Ini adalah versi manual/visual dari konsep beforeEach yang kita pakai di Jest pada artikel sebelumnya.

1.5 Dokumentasi Screenshot Pengujian Postman

Berikut hasil pengujian tiap endpoint yang sudah dilakukan langsung di Postman, sebagai referensi visual supaya kamu bisa membandingkan hasil di komputermu sendiri dengan hasil yang seharusnya didapat.

1.5.1 POST /api/auth/register

endpoint register

Body request (name, email, password) dan response 201 dengan data user & token yang berhasil dibuat.

1.5.2 POST /api/auth/login

script postman login

Kode Scripts -> Post-response yang otomatis simpan response token ke enviroment.

postman login

Body request (email, password), response 200, sekaligus variabel token yang otomatis tersimpan di tab Environment.

1.5.3 POST /api/todos (Create Todo)

tab authorization create todoendpoint create todos

Tab Authorization dengan Bearer Token {{token}}, body request (title, description), dan response 201 dengan data todo yang berhasil dibuat.

1.5.4 GET /api/todos (Get All Todos)

postman get all

Tab Params berisi query page, limit, completed, dan response 200 lengkap dengan object pagination.

1.5.5 GET /api/todos/:id (Get Todo By ID)

postman get by id

URL dengan :id terisi ID todo sungguhan, dan response 200 berisi detail satu todo.

1.5.6 PUT /api/todos/:id (Update Todo)

postman update todo

Body request berisi field yang diubah (misalnya completed: true) dan response 200 dengan data yang sudah ter-update.

1.5.7 DELETE /api/todos/:id (Delete Todo)

postman delete todoPOSTMAN setelah delete

Response 200 setelah todo dihapus, dan percobaan GET ke ID yang sama setelahnya yang membuktikan datanya sudah benar-benar hilang (404).

1.5.8 GET /api/stats/summary (dengan API Key)

postman get summary

Header x-api-key berisi {{apiKey}} di tab Headers, dan response 200 berisi totalTodos, completedTodos, pendingTodos.


2. Swagger: Mendokumentasikan API

Analogi: Postman collection bagus untuk menguji, tapi biasanya hanya dipegang oleh tim internal. Swagger (OpenAPI) berperan seperti papan informasi resmi di depan sekolah yang bisa dibaca siapa saja — tamu, wali murid, calon siswa baru — berisi daftar lengkap “layanan apa saja yang tersedia”, syarat mengaksesnya, dan formulir apa yang harus diisi, tanpa mereka perlu bertanya langsung ke petugas satu-satu.

2.1 Install Dependency

npm install swagger-jsdoc swagger-ui-express

Penjelasan teknis:

  • swagger-jsdoc — membaca komentar khusus (format JSDoc) yang kita tulis di atas route, lalu mengubahnya jadi spesifikasi OpenAPI (dokumen JSON terstruktur).

  • swagger-ui-express — mengambil spesifikasi OpenAPI itu dan menampilkannya sebagai halaman web interaktif yang bisa dicoba langsung dari browser.

2.2 Membuat Konfigurasi Swagger

mkdir -p src/config

src/config/swagger.js

const swaggerJSDoc = require("swagger-jsdoc");
 
const options = {
  definition: {
    openapi: "3.0.0",
    info: {
      title: "Todo List API",
      version: "1.0.0",
      description: "Dokumentasi API Todo List — dibangun bertahap dari seri artikel backend Node.js",
    },
    servers: [
      {
        url: "http://localhost:3000",
        description: "Local development server",
      },
    ],
    components: {
      securitySchemes: {
        bearerAuth: {
          type: "http",
          scheme: "bearer",
          bearerFormat: "JWT",
        },
        apiKeyAuth: {
          type: "apiKey",
          in: "header",
          name: "x-api-key",
        },
      },
    },
  },
  // Swagger akan mencari komentar dokumentasi di semua file route
  apis: ["./src/routes/*.js"],
};
 
const swaggerSpec = swaggerJSDoc(options);
 
module.exports = swaggerSpec;

Penjelasan teknis:

  • components.securitySchemes mendefinisikan dua jenis kunci yang dikenali dokumentasi ini: bearerAuth merepresentasikan token JWT dari artikel autentikasi, dan apiKeyAuth merepresentasikan header x-api-key dari artikel API Key. Nanti tiap endpoint tinggal “menunjuk” skema mana yang dipakainya, tanpa perlu menjelaskan ulang cara kerja autentikasi di setiap endpoint.

  • apis: ["./src/routes/*.js"] adalah instruksi ke swagger-jsdoc: “baca semua file di folder routes, cari komentar dokumentasi di sana.” Artinya dokumentasi kita akan hidup berdampingan langsung dengan kode route-nya, bukan di file terpisah yang gampang lupa di-update.

2.3 Menulis Dokumentasi di Atas Route Auth

src/routes/auth.routes.js (update penuh, dengan komentar dokumentasi)

const express = require("express");
const router = express.Router();
const authController = require("../controllers/auth.controller");
 
/**
 * @swagger
 * /api/auth/register:
 *   post:
 *     summary: Mendaftarkan user baru
 *     tags: [Auth]
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required: [name, email, password]
 *             properties:
 *               name:
 *                 type: string
 *                 example: Budi
 *               email:
 *                 type: string
 *                 example: budi@example.com
 *               password:
 *                 type: string
 *                 example: rahasia123
 *     responses:
 *       201:
 *         description: User berhasil didaftarkan
 *       400:
 *         description: Data tidak lengkap
 *       409:
 *         description: Email sudah terdaftar
 */
router.post("/register", authController.register);
 
/**
 * @swagger
 * /api/auth/login:
 *   post:
 *     summary: Login dan mendapatkan JWT token
 *     tags: [Auth]
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required: [email, password]
 *             properties:
 *               email:
 *                 type: string
 *                 example: budi@example.com
 *               password:
 *                 type: string
 *                 example: rahasia123
 *     responses:
 *       200:
 *         description: Login berhasil, token dikembalikan
 *       401:
 *         description: Email atau password salah
 */
router.post("/login", authController.login);
 
module.exports = router;

Penjelasan teknis:

  • Komentar ini bukan komentar biasa — formatnya harus diawali /** dan tag @swagger, karena itulah tanda yang dicari oleh swagger-jsdoc saat membaca file. Isinya ditulis dalam format YAML, mengikuti standar spesifikasi OpenAPI.

  • tags: [Auth] menentukan endpoint ini akan dikelompokkan di bawah kategori “Auth” saat ditampilkan di halaman Swagger UI — mirip konsep folder Auth yang kita buat di Postman pada bagian sebelumnya.

  • requestBody.content.application/json.schema mendeskripsikan bentuk data yang harus dikirim, field mana yang wajib (required), dan contoh nilainya (example) — ini yang membuat Swagger UI bisa menampilkan formulir siap-isi otomatis di browser.

  • Bagian responses mendaftarkan kemungkinan status code yang bisa dikembalikan endpoint ini, cocok dengan status code yang benar-benar kita implementasikan lewat AppError di artikel error handling (400, 409, dst).

2.4 Menulis Dokumentasi di Atas Route Todo (dengan Autentikasi)

Supaya tidak berulang-ulang menulis skema Todo secara manual di setiap endpoint, kita definisikan dulu reusable schema component di src/config/swagger.js, lalu tinggal dipanggil (referensi) di tiap route.

src/config/swagger.js (tambahkan schemas di dalam components, sejajar dengan securitySchemes yang sudah dibuat di poin 2.2)

components: {
  securitySchemes: {
    bearerAuth: {
      type: "http",
      scheme: "bearer",
      bearerFormat: "JWT",
    },
    apiKeyAuth: {
      type: "apiKey",
      in: "header",
      name: "x-api-key",
    },
  },
  schemas: {
    Todo: {
      type: "object",
      properties: {
        _id: { type: "string", example: "665f1c2e8b1e2a1a2c3d4e5f" },
        title: { type: "string", example: "Belajar Swagger" },
        description: { type: "string", example: "Menulis dokumentasi endpoint todo" },
        completed: { type: "boolean", example: false },
        owner: { type: "string", example: "665f1a2b8b1e2a1a2c3d1111" },
        createdAt: { type: "string", format: "date-time" },
        updatedAt: { type: "string", format: "date-time" },
      },
    },
  },
},

Penjelasan teknis: mendefinisikan schemas.Todo di sini artinya kita cukup menulis bentuk data todo satu kali saja. Di tiap endpoint nanti, kita tinggal menulis referensi $ref: "#/components/schemas/Todo" alih-alih menyalin ulang daftar properties setiap kali — persis prinsip DRY (Don't Repeat Yourself) yang sudah kita terapkan di kode sungguhan sejak artikel pertama (misalnya lewat catchAsync dan AppError). Kalau nanti struktur model Todo berubah (misal ada field baru), kita cukup update satu tempat ini, dan seluruh dokumentasi endpoint yang mereferensikannya ikut ter-update otomatis.

Sekarang lengkapi dokumentasi untuk seluruh route di todo.routes.js:

src/routes/todo.routes.js (update penuh, dengan komentar dokumentasi Swagger di setiap route)

const express = require("express");
const router = express.Router();
const todoController = require("../controllers/todo.controller");
const { protect } = require("../middlewares/auth.middleware");
const validate = require("../middlewares/validate.middleware");
const {
  createTodoRules,
  updateTodoRules,
  getTodoByIdRules,
  getAllTodosRules,
} = require("../validators/todo.validator");
 
router.use(protect);
 
/**
 * @swagger
 * /api/todos:
 *   post:
 *     summary: Membuat todo baru (wajib login)
 *     tags: [Todos]
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required: [title]
 *             properties:
 *               title:
 *                 type: string
 *                 example: Belajar Swagger
 *               description:
 *                 type: string
 *                 example: Menulis dokumentasi endpoint todo
 *     responses:
 *       201:
 *         description: Todo berhasil dibuat
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success: { type: boolean, example: true }
 *                 message: { type: string, example: Todo created successfully }
 *                 data:
 *                   $ref: '#/components/schemas/Todo'
 *       400:
 *         description: Validasi gagal (misal title kosong atau kurang dari 3 karakter)
 *       401:
 *         description: Belum login / token tidak valid
 */
router.post("/", createTodoRules, validate, todoController.createTodo);
 
/**
 * @swagger
 * /api/todos:
 *   get:
 *     summary: Mengambil daftar todo milik user yang sedang login (dengan pagination)
 *     tags: [Todos]
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *           minimum: 1
 *         description: Nomor halaman
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           minimum: 1
 *           maximum: 100
 *         description: Jumlah data per halaman
 *       - in: query
 *         name: completed
 *         schema:
 *           type: boolean
 *         description: Filter berdasarkan status selesai
 *       - in: query
 *         name: sortBy
 *         schema:
 *           type: string
 *           enum: [createdAt, title, completed]
 *         description: Field yang dipakai untuk mengurutkan data
 *       - in: query
 *         name: order
 *         schema:
 *           type: string
 *           enum: [asc, desc]
 *         description: Arah pengurutan data
 *     responses:
 *       200:
 *         description: Daftar todo berhasil diambil
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success: { type: boolean, example: true }
 *                 message: { type: string, example: Todos retrieved successfully }
 *                 data:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/Todo'
 *                 pagination:
 *                   type: object
 *                   properties:
 *                     currentPage: { type: integer, example: 1 }
 *                     totalPages: { type: integer, example: 3 }
 *                     totalItems: { type: integer, example: 25 }
 *                     itemsPerPage: { type: integer, example: 10 }
 *       401:
 *         description: Belum login / token tidak valid
 */
router.get("/", getAllTodosRules, validate, todoController.getAllTodos);
 
/**
 * @swagger
 * /api/todos/{id}:
 *   get:
 *     summary: Mengambil satu todo berdasarkan ID
 *     tags: [Todos]
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: ID todo (format ObjectId MongoDB)
 *     responses:
 *       200:
 *         description: Todo berhasil ditemukan
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success: { type: boolean, example: true }
 *                 message: { type: string, example: Todo retrieved successfully }
 *                 data:
 *                   $ref: '#/components/schemas/Todo'
 *       400:
 *         description: Format ID tidak valid
 *       401:
 *         description: Belum login / token tidak valid
 *       403:
 *         description: Todo ini bukan milik user yang sedang login
 *       404:
 *         description: Todo tidak ditemukan
 */
router.get("/:id", getTodoByIdRules, validate, todoController.getTodoById);
 
/**
 * @swagger
 * /api/todos/{id}:
 *   put:
 *     summary: Mengupdate todo berdasarkan ID
 *     tags: [Todos]
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: ID todo (format ObjectId MongoDB)
 *     requestBody:
 *       required: false
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               title:
 *                 type: string
 *                 example: Belajar Swagger (revisi)
 *               description:
 *                 type: string
 *                 example: Menambahkan contoh response schema
 *               completed:
 *                 type: boolean
 *                 example: true
 *     responses:
 *       200:
 *         description: Todo berhasil diupdate
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success: { type: boolean, example: true }
 *                 message: { type: string, example: Todo updated successfully }
 *                 data:
 *                   $ref: '#/components/schemas/Todo'
 *       400:
 *         description: Validasi gagal atau format ID tidak valid
 *       401:
 *         description: Belum login / token tidak valid
 *       403:
 *         description: Todo ini bukan milik user yang sedang login
 *       404:
 *         description: Todo tidak ditemukan
 */
router.put("/:id", updateTodoRules, validate, todoController.updateTodo);
 
/**
 * @swagger
 * /api/todos/{id}:
 *   delete:
 *     summary: Menghapus todo berdasarkan ID
 *     tags: [Todos]
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: ID todo (format ObjectId MongoDB)
 *     responses:
 *       200:
 *         description: Todo berhasil dihapus
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success: { type: boolean, example: true }
 *                 message: { type: string, example: Todo deleted successfully }
 *                 data:
 *                   $ref: '#/components/schemas/Todo'
 *       400:
 *         description: Format ID tidak valid
 *       401:
 *         description: Belum login / token tidak valid
 *       403:
 *         description: Todo ini bukan milik user yang sedang login
 *       404:
 *         description: Todo tidak ditemukan
 */
router.delete("/:id", getTodoByIdRules, validate, todoController.deleteTodo);
 
module.exports = router;

Penjelasan teknis:

  • security: [- bearerAuth: []] menghubungkan endpoint ini ke skema bearerAuth yang kita definisikan di swagger.js (poin 2.2). Efeknya, Swagger UI akan menampilkan ikon gembok di endpoint ini dan menyediakan kolom untuk memasukkan token — representasi visual dari middleware protect yang sesungguhnya berjalan di baliknya lewat router.use(protect) di baris paling atas.

  • parameters dengan in: query mendokumentasikan parameter ?page=, ?limit=, ?completed=, ?sortBy=, ?order= yang kita implementasikan di artikel validasi dan pagination. Nilai enum pada sortBy dan order di dokumentasi ini sengaja disamakan persis dengan aturan isIn([...]) yang ada di getAllTodosRules pada todo.validator.js — supaya dokumentasi tidak pernah berbohong soal nilai apa saja yang sebenarnya diterima server.

  • parameters dengan in: path (dipakai di GET /:id, PUT /:id, DELETE /:id) berbeda dengan in: query — ini mendokumentasikan bagian URL yang berupa segmen path, misalnya id pada /api/todos/665f1c2e..., bukan bagian setelah tanda tanya (?). Perhatikan juga penulisan di baris /api/todos/{id}: memakai kurung kurawal {id}, ini adalah cara OpenAPI menuliskan path parameter, berbeda dari :id yang dipakai Express di kode routing sungguhannya.

  • $ref: '#/components/schemas/Todo' adalah cara memanggil kembali skema Todo yang sudah kita definisikan sekali di swagger.js. Ini yang membuat Swagger UI bisa menampilkan struktur data todo secara konsisten di semua endpoint (create, get, update, delete) tanpa kita menulis ulang daftar field yang sama berkali-kali.

  • Bagian responses di tiap route sengaja didaftarkan lengkap sesuai status code yang benar-benar mungkin dikembalikan — termasuk 403 untuk kasus “todo ini bukan milik user yang login” dari artikel autentikasi JWT, dan 404 untuk kasus “todo tidak ditemukan” dari artikel error handling. Dokumentasi yang mendaftarkan skenario error secara lengkap seperti ini jauh lebih membantu dibanding hanya menyebut “200: OK” tanpa penjelasan apa saja yang bisa gagal.

2.5 Menulis Dokumentasi untuk Endpoint Stats (API Key)

Endpoint /api/stats/summary dari artikel proteksi API Key memakai mekanisme autentikasi yang berbeda dari endpoint todo — dia tidak butuh bearerAuth (JWT), melainkan apiKeyAuth yang sudah kita definisikan di swagger.js pada poin 2.2. Ini kesempatan bagus untuk melihat bagaimana Swagger mendokumentasikan dua jenis proteksi yang berbeda di satu project yang sama.

src/routes/stats.routes.js (update penuh, dengan komentar dokumentasi Swagger)

const express = require("express");
const router = express.Router();
const statsController = require("../controllers/stats.controller");
const checkApiKey = require("../middlewares/apiKey.middleware");
 
/**
 * @swagger
 * /api/stats/summary:
 *   get:
 *     summary: Mengambil ringkasan statistik todo (total, selesai, belum selesai)
 *     description: >
 *       Endpoint ini ditujukan untuk integrasi machine-to-machine (misalnya dashboard
 *       eksternal), sehingga tidak memakai login JWT seperti endpoint /api/todos,
 *       melainkan proteksi API Key lewat header x-api-key.
 *     tags: [Stats]
 *     security:
 *       - apiKeyAuth: []
 *     responses:
 *       200:
 *         description: Ringkasan statistik berhasil diambil
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success: { type: boolean, example: true }
 *                 message: { type: string, example: Summary retrieved successfully }
 *                 data:
 *                   type: object
 *                   properties:
 *                     totalTodos: { type: integer, example: 25 }
 *                     completedTodos: { type: integer, example: 10 }
 *                     pendingTodos: { type: integer, example: 15 }
 *       401:
 *         description: API key tidak dikirim atau tidak valid
 */
router.get("/summary", checkApiKey, statsController.getSummary);
 
module.exports = router;

Penjelasan teknis:

  • security: [- apiKeyAuth: []] ini yang membedakan endpoint ini secara visual di Swagger UI dari endpoint /api/todos — ikon gembok yang muncul di sini terhubung ke skema apiKeyAuth (header x-api-key), bukan bearerAuth (header Authorization: Bearer). Kalau nanti kamu klik tombol Authorize di Swagger UI, akan muncul dua kolom input terpisah: satu untuk token JWT, satu lagi untuk API key — masing-masing otomatis dipasang ke endpoint yang sesuai kebutuhannya.

  • Field description yang ditulis dengan format > (YAML block scalar) dipakai supaya kalimat penjelasan yang panjang bisa ditulis dalam beberapa baris di kode, tapi tetap dirender sebagai satu paragraf utuh di halaman Swagger UI. Ini dipakai khusus di endpoint stats karena penjelasannya perlu menekankan kenapa endpoint ini beda mekanisme auth-nya dari endpoint lain — sesuatu yang tidak cukup dijelaskan hanya lewat summary satu baris.

  • Endpoint ini tidak punya 403 di daftar responses-nya, berbeda dari endpoint todo. Ini konsisten dengan implementasi apiKey.middleware.js di artikel proteksi API Key: karena tidak ada konsep “kepemilikan data” untuk API key (beda dengan owner pada todo), maka satu-satunya kemungkinan gagal adalah 401 — key-nya tidak ada atau salah, titik.

2.6 Menampilkan Swagger UI di Aplikasi

src/app.js (update penuh)

const express = require("express");
const swaggerUi = require("swagger-ui-express");
const swaggerSpec = require("./config/swagger");
const todoRoutes = require("./routes/todo.routes");
const authRoutes = require("./routes/auth.routes");
const statsRoutes = require("./routes/stats.routes");
const logger = require("./middlewares/logger.middleware");
const notFound = require("./middlewares/notFound.middleware");
const errorHandler = require("./middlewares/errorHandler.middleware");
 
const app = express();
 
app.use(logger);
app.use(express.json());
 
app.get("/", (req, res) => {
  res.json({ message: "Todo API is running" });
});
 
// Halaman dokumentasi interaktif tersedia di /api-docs
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
 
app.use("/api/auth", authRoutes);
app.use("/api/todos", todoRoutes);
app.use("/api/stats", statsRoutes);
 
app.use(notFound);
app.use(errorHandler);
 
module.exports = app;

Penjelasan teknis:

  • swaggerUi.serve menyiapkan file statis (HTML, CSS, JS) yang dibutuhkan untuk menampilkan halaman Swagger UI, sedangkan swaggerUi.setup(swaggerSpec) mengisi halaman itu dengan data spesifikasi yang sudah kita bangun dari komentar-komentar JSDoc di poin 2.3 dan 2.4.

  • Route ini sengaja diletakkan sebelum notFound dan errorHandler, sama seperti pola pemasangan middleware yang sudah kita pelajari di artikel kedua: route/handler yang sungguhan harus terdaftar sebelum “jaring penangkap” di paling akhir.


3. Struktur Folder Setelah Artikel Ini

todo-api/
├── src/
│   ├── config/
│   │   ├── db.js
│   │   └── swagger.js
│   ├── middlewares/
│   │   ├── logger.middleware.js
│   │   ├── notFound.middleware.js
│   │   ├── errorHandler.middleware.js
│   │   ├── auth.middleware.js
│   │   ├── restrictTo.middleware.js
│   │   ├── apiKey.middleware.js
│   │   └── validate.middleware.js
│   ├── validators/
│   │   └── todo.validator.js
│   ├── models/
│   │   ├── todo.model.js
│   │   └── user.model.js
│   ├── services/
│   │   ├── todo.service.js
│   │   └── auth.service.js
│   ├── controllers/
│   │   ├── todo.controller.js
│   │   ├── auth.controller.js
│   │   └── stats.controller.js
│   ├── routes/
│   │   ├── todo.routes.js
│   │   ├── auth.routes.js
│   │   └── stats.routes.js
│   ├── utils/
│   │   ├── AppError.js
│   │   └── catchAsync.js
│   ├── app.js
│   └── server.js
├── tests/
│   ├── setup.js
│   ├── auth.test.js
│   ├── todo.test.js
│   └── stats.test.js
├── jest.config.js
├── .env
├── .env.test
├── .env.example
├── .gitignore
└── package.json

4. Menguji Hasil Akhir

npm run dev

Buka browser ke http://localhost:3000/api-docs. Kamu akan melihat halaman Swagger UI dengan tiga kategori (Auth, Todos, Stats), lengkap dengan tombol Try it out di tiap endpoint untuk mencoba langsung dari browser tanpa perlu Postman atau curl sama sekali.

Penjelasan teknis: tombol Authorize di pojok kanan atas halaman Swagger UI muncul otomatis karena kita mendefinisikan securitySchemes di swagger.js. Setelah kamu klik tombol itu dan memasukkan token JWT hasil login, Swagger UI akan otomatis menyertakan header Authorization: Bearer <token> di setiap request “Try it out” yang butuh autentikasi — jadi kamu tidak perlu memasukkan token berulang-ulang di setiap endpoint satu-satu.


5. Contoh pengujian Swagger

Berikut contoh tampilan lengkap pengujian backend todo dengan swagger:


6. Perbandingan Sebelum dan Sesudah

Aspek

Sebelum

Sesudah

Cara tim lain mencoba API

Harus tanya langsung / baca kode

Bisa coba sendiri lewat Postman collection atau Swagger UI

Dokumentasi endpoint

Tidak ada, tersebar di kepala developer

Terpusat & hidup berdampingan dengan kode (JSDoc)

Auth saat testing manual

Copy-paste token manual tiap request

Otomatis lewat environment variable / tombol Authorize

Onboarding developer baru

Lambat, banyak tanya ke tim lain

Bisa mulai eksplorasi sendiri lewat /api-docs


7. Kesimpulan

Dengan Postman collection yang terstruktur dan dokumentasi Swagger yang hidup berdampingan dengan kode, Todo List API kita sekarang mudah diuji secara manual maupun dieksplorasi oleh siapa pun tanpa harus membaca kode sumber atau bertanya langsung ke developer yang membuatnya. Ini melengkapi automated test dari artikel sebelumnya — automated test menjaga kebenaran logika, sementara Postman dan Swagger menjaga kemudahan kolaborasi.

Sampai di sini, Todo List API kita sudah memiliki hampir semua elemen backend yang layak produksi: struktur folder rapi, middleware, error handling, autentikasi, API key, validasi, pagination, automated test, dan dokumentasi. Artikel terakhir dalam seri ini akan merangkum semuanya menjadi satu boilerplate utuh yang siap dipakai sebagai starter project.

Lanjutkan ke artikel terakhir: Struktur Project Node.js Siap Produksi: Boilerplate Backend dari Nol

Artikel Terkait

Panduan Membuat Animasi Animated on Scroll di Website Anda

Pelajari cara membuat animasi menarik pada scroll di website Anda dengan tutorial ini. Tampilkan konten dengan cara yang lebih interaktif!

Baca Artikel
Cara Menangani Error dan HTTP Status Code yang Benar

cara menentukan HTTP status code yang benar untuk tiap jenis error, dan cara membuat custom Error

Baca Artikel
Cara Restrukturisasi CRUD Node.js: Dari Function ke Folder Controller-Service-Model

cara merestrukturisasi kode CRUD dari kumpulan function menjadi struktur folder yang rapi menggunakan pola Controller-Service-Model.

Baca Artikel