Testing dan Dokumentasi API dengan Postman dan Swagger
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 |
|---|---|---|
|
| Alamat dasar server |
| (kosongkan) | Akan diisi otomatis setelah login |
| isi dengan nilai | Untuk endpoint stats |
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);
});
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.
Penjelasan teknis:
pm.test(nama, function)adalah cara Postman mendefinisikan satu kasus pengujian — konsepnya mirip persis denganit("...", () => {...})yang kita tulis di Jest pada artikel sebelumnya, hanya beda platform.pm.response.json()mengambil body response dan mem-parsingnya jadi object JavaScript, sama sepertires.bodyyang 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:
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
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
Body request (name, email, password) dan response 201 dengan data user & token yang berhasil dibuat.
1.5.2 POST /api/auth/login
Kode Scripts -> Post-response yang otomatis simpan response token ke enviroment.
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 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)
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)
URL dengan :id terisi ID todo sungguhan, dan response 200 berisi detail satu todo.
1.5.6 PUT /api/todos/:id (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)
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)
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.securitySchemesmendefinisikan dua jenis kunci yang dikenali dokumentasi ini:bearerAuthmerepresentasikan token JWT dari artikel autentikasi, danapiKeyAuthmerepresentasikan headerx-api-keydari 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 keswagger-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 olehswagger-jsdocsaat 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 folderAuthyang kita buat di Postman pada bagian sebelumnya.requestBody.content.application/json.schemamendeskripsikan 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
responsesmendaftarkan kemungkinan status code yang bisa dikembalikan endpoint ini, cocok dengan status code yang benar-benar kita implementasikan lewatAppErrordi 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 skemabearerAuthyang kita definisikan diswagger.js(poin 2.2). Efeknya, Swagger UI akan menampilkan ikon gembok di endpoint ini dan menyediakan kolom untuk memasukkan token — representasi visual dari middlewareprotectyang sesungguhnya berjalan di baliknya lewatrouter.use(protect)di baris paling atas.parametersdenganin: querymendokumentasikan parameter?page=,?limit=,?completed=,?sortBy=,?order=yang kita implementasikan di artikel validasi dan pagination. NilaienumpadasortBydanorderdi dokumentasi ini sengaja disamakan persis dengan aturanisIn([...])yang ada digetAllTodosRulespadatodo.validator.js— supaya dokumentasi tidak pernah berbohong soal nilai apa saja yang sebenarnya diterima server.parametersdenganin: path(dipakai diGET /:id,PUT /:id,DELETE /:id) berbeda denganin: query— ini mendokumentasikan bagian URL yang berupa segmen path, misalnyaidpada/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:idyang dipakai Express di kode routing sungguhannya.$ref: '#/components/schemas/Todo'adalah cara memanggil kembali skemaTodoyang sudah kita definisikan sekali diswagger.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
responsesdi tiap route sengaja didaftarkan lengkap sesuai status code yang benar-benar mungkin dikembalikan — termasuk403untuk kasus “todo ini bukan milik user yang login” dari artikel autentikasi JWT, dan404untuk 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 skemaapiKeyAuth(headerx-api-key), bukanbearerAuth(headerAuthorization: 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
descriptionyang 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 lewatsummarysatu baris.Endpoint ini tidak punya
403di daftarresponses-nya, berbeda dari endpoint todo. Ini konsisten dengan implementasiapiKey.middleware.jsdi artikel proteksi API Key: karena tidak ada konsep “kepemilikan data” untuk API key (beda denganownerpada todo), maka satu-satunya kemungkinan gagal adalah401— 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.servemenyiapkan file statis (HTML, CSS, JS) yang dibutuhkan untuk menampilkan halaman Swagger UI, sedangkanswaggerUi.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
notFounddanerrorHandler, 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!
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
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.
