Dokumentasi API dengan Swagger & OpenAPI

12 min readIntermediate
SwaggerOpenAPIDokumentasiAPI

Kita sudah membahas bagaimana REST API dirancang di Rest API Design Guidelines. Sekarang muncul pertanyaan berikutnya: setelah API selesai dibangun, bagaimana orang lain tahu cara memakainya? Jawaban idealnya bukan lewat dokumen Word yang cepat basi, melainkan lewat dokumentasi yang di-generate langsung dari kode. Artikel ini membahas dua nama yang sering tertukar — Swagger dan OpenAPI — dan bagaimana springdoc-openapi membuat dokumentasi API Spring Boot muncul otomatis sekaligus bisa diuji langsung dari browser.

Kenapa Dokumentasi API Penting

API yang tak terdokumentasi praktis tidak bisa dipakai. Klien (frontend, aplikasi mobile, atau tim lain) tidak bisa menebak bahwa endpoint bernama GET /api/v1/orders memerlukan parameter status bertipe enum, atau bahwa POST /api/v1/orders mengembalikan 201 dengan struktur {orderId, amount, status}. Setiap kali mereka menebak, ada kemungkinan salah — dan kesalahan itu berujung pada iterasi bolak-balik yang membuang waktu.

Masalah dokumentasi manual (menulis Markdown/Word terpisah dari kode) kira-kira seperti ini:

  1. Cepat usang — kode diubah oleh developer, tapi dokumen lupa diperbarui. Dokumen yang salah lebih berbahaya daripada tidak ada dokumen, karena memberi kepastian yang keliru.
  2. Beda-beda antar tim — tiap orang punya gaya sendiri; satu tim menulis deskripsi bertele-tele, tim lain cuma menulis satu baris. Tidak ada format standar untuk diprogram.
  3. Tidak bisa diuji — dokumentasi manual biasanya hanya "teks", tidak punya tombol untuk mencoba endpoint langsung.

Solusinya: generate dokumentasi dari kode. Kontrak (spesifikasi) dan implementasi berada di satu tempat, sehingga keduanya selalu sinkron — kalau kode berubah, spesifikasi ikut berubah. Inilah yang dilakukan springdoc-openapi di ekosistem Spring Boot.

OpenAPI Specification

OpenAPI Specification adalah standar terbuka untuk mendeskripsikan sebuah API — dulu dikenal sebagai Swagger 2.0, lalu berganti nama menjadi OpenAPI setelah dipindah ke OpenAPI Initiative pada tahun 2015. Spesifikasinya ditulis dalam format JSON atau YAML dan bisa dibaca manusia maupun mesin.

Isi sebuah dokumen OpenAPI pada dasarnya menjawab empat pertanyaan:

BagianYang dijelaskan
pathsEndpoint apa saja yang tersedia, misal /users, /orders/{id}
methodsUntuk tiap path: operasi get, post, put, delete, dan sebagainya
parametersParameter path, query, header, atau cookie yang dibutuhkan tiap operasi
requestBody & responsesSkema (struktur JSON) dari data yang dikirim dan data yang dikembalikan
components / securitySchemesDefinisi ulang skema (schemas), reusable components, dan mekanisme keamanan seperti Bearer token

Contoh kecil dokumen OpenAPI dalam YAML untuk sebuah endpoint sederhana:

openapi: 3.0.3
info:
  title: Order Service API
  version: 1.0.0
  description: API untuk membaca dan membuat pesanan.
paths:
  /orders:
    get:
      summary: Daftar pesanan
      parameters:
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum: [NEW, PAID, SHIPPED]
      responses:
        "200":
          description: Daftar pesanan berhasil diambil
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Order"
    post:
      summary: Membuat pesanan baru
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrderInput"
      responses:
        "201":
          description: Pesanan berhasil dibuat
components:
  schemas:
    Order:
      type: object
      properties:
        id: { type: integer, format: int64 }
        amount: { type: number }
        status: { type: string }
    OrderInput:
      type: object
      required: [amount]
      properties:
        amount: { type: number }

Perhatikan bagaimana $ref dipakai untuk menyebut skema yang sama (Order) dari beberapa tempat — ini reusable component yang menghindari pengulangan. Justru dokumen YAML/JSON seperti inilah yang nantinya dihasilkan secara otomatis oleh springdoc-openapi dari kode Anda.

Mengenal Ekosistem Istilah: Swagger vs OpenAPI

Nama Swagger dan OpenAPI sering dipakai bergantian, padahal keduanya berbeda:

IstilahItu apa?Contoh
OpenAPI SpecificationStandar/spesifikasi — aturan format deskripsi APIFile openapi.yaml
SwaggerTooling/ekosistem dari SmartBear yang awalnya menciptakan standar tersebutSwagger Editor, Swagger Codegen
Swagger UIAntarmuka web yang menampilkan spesifikasi sebagai halaman interaktif yang bisa dieksplorasi dan diujiHalaman /swagger-ui.html
springdoc-openapiLibrary untuk memproduksi dokumentasi OpenAPI otomatis dari aplikasi Spring Boot, sekaligus menghidangkan Swagger UIDependency di pom.xml
openapi-generatorGenerator kode dari spesifikasi (client, server, atau doc)openapi-generator-cli

Aturan praktis untuk mengingatnya: OpenAPI adalah bahasanya, Swagger adalah alat-alatnya. Library springdoc-openapi menjembatani keduanya — ia membaca kode Spring Anda, menghasilkan dokumen OpenAPI, lalu menampilkannya lewat Swagger UI.

Implementasi di Spring Boot dengan springdoc-openapi

Cara paling cepat menyalakan dokumentasi di Spring Boot adalah menambahkan satu dependency. Untuk Spring Boot 3.x (pakai jakarta), tambahkan pada pom.xml:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.8.6</version>
</dependency>

Satu baris dependency ini langsung memberi dua endpoint gratis (lihat Springdoc documentation):

EndpointIsi
GET /v3/api-docsDokumen OpenAPI mentah dalam JSON (spesifikasinya)
GET /swagger-ui.htmlHalaman interaktif Swagger UI untuk mengeksplorasi dan menguji API

Jalankan aplikasi, buka http://localhost:8080/swagger-ui.html — semua @RestController Anda sudah tampil dengan skema request/response yang otomatis dideteksi. Tanpa menulis satu baris anotasi pun, dokumentasi dasar sudah jadi.

Menyesuaikan Metadata: Bean OpenAPI

Dokumentasi "bawaan" biasanya polos. Untuk memberi title, version, description, dan konfigurasi security scheme, daftarkan sebuah bean OpenAPI:

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI orderServiceOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("Order Service API")
                        .version("1.0.0")
                        .description("API untuk membaca dan membuat pesanan.")
                        .license(new License().name("MIT")))
                .addServersItem(new Server().url("/").description("Server default"));
    }
}

Skema security scheme diatur lewat @SecurityScheme, biasanya diletakkan pada class @Configuration:

import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme;

@SecurityScheme(
        name = "bearerAuth",
        type = SecuritySchemeType.HTTP,
        scheme = "bearer",
        bearerFormat = "JWT",
        description = "Masukkan token JWT: diawali dengan 'Bearer '")
@Configuration
public class OpenApiSecurityConfig {
    // beans tetap di sini
}

bearerAuth adalah nama (kunci) yang nantinya dipakai oleh anotasi @SecurityRequirement(name = "bearerAuth") pada operasi yang butuh autentikasi.

Anotasi Deskriptif: Membuat UI Rapi

Anotasi springdoc yang paling penting adalah @Operation, @ApiResponse, dan @Tag. Ketiganya tidak mengubah perilaku kode — fungsinya murni membuat dokumentasi yang dihasilkan lebih informatif.

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Tag(name = "Orders", description = "Operasi pengelolaan pesanan")
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    @Operation(
            summary = "Ambil detail pesanan",
            description = "Mengembalikan detail pesanan berdasarkan id. "
                    + "Mengembalikan 404 jika id tidak ditemukan.")
    @ApiResponse(responseCode = "200",
            description = "Pesanan ditemukan",
            content = @Content(schema = @Schema(implementation = OrderDto.class)))
    @ApiResponse(responseCode = "404",
            description = "Pesanan tidak ditemukan")
    @ApiResponse(responseCode = "401",
            description = "Token tidak valid atau belum login")
    @GetMapping("/{id}")
    public ResponseEntity<OrderDto> getOrder(@PathVariable Long id) {
        // panggilan ke service, dsb.
        return ResponseEntity.ok(orderService.findById(id));
    }
}

Mengapa ini penting? Tanpa anotasi, Swagger UI hanya menampilkan nama method Java (getOrder) dan status default yang tak berarti. Dengan @Operation dan @ApiResponse, klien melihat summary yang jelas, deskripsi error yang jujur (404, 401), dan skema yang benar — tanpa perlu membaca kode sumber.

@Tag juga membantu mengelompokkan endpoint yang berkaitan. Alih-alih daftar menu datar, klien melihat kategori seperti Orders, Customers, dan Payments — jauh lebih mudah dinavigasi untuk API yang besar.

Skema Otomatis dari DTO (Jangan dari Entitas)

springdoc menyimpulkan skema request/response dari tipe yang dipakai di controller. Kalau tipe itu adalah entitas JPA, inilah saatnya Anda hati-hati:

  • Entitas bisa berisi field internal seperti passwordHash, version, createdAt yang tidak ingin diekspos.
  • Entitas punya relasi lazy (@ManyToOne, @OneToMany) yang saat diserialisasi di luar transaksi bisa melempar LazyInitializationException.
  • Struktur entitas sering tidak sama dengan bentuk JSON yang memang ingin Anda jual ke klien.

Praktik yang benar: pakai DTO (Data Transfer Object). Bentuk DTO sesuai dengan bentuk JSON yang dijanjikan, dan tidak pernah membocorkan field internal.

public record OrderDto(
        Long id,
        BigDecimal amount,
        String status,
        String customerEmail) {
}

Entitas ke DTO dipetakan di layer service/mapper, bukan di controller langsung. Dengan pola ini, dokumentasi yang muncul di Swagger UI otomatis menjadi dokumen publik yang aman, bukan dump dari struktur database.

Memakai Swagger UI untuk Menguji Endpoint

Keunggulan terbesar Swagger UI adalah ia bukan sekadar "katalog", melainkan juga client untuk mencoba API langsung dari browser:

  1. Explore — setiap operasi tampil sebagai blok dengan summary, parameters, dan contoh response. Buka salah satu blok untuk melihat detail.
  2. "Try it out" — klik tombol ini untuk mengaktifkan mode pengujian; kolom parameter menjadi dapat diedit.
  3. Isi parameter — masukkan nilai path/query, dan untuk POST/PUT isi body JSON di editor.
  4. Autentikasi — klik tombol Authorize di kanan atas, tempel token (misalnya Bearer <token>), dan semua request akan membawa header Authorization secara otomatis.
  5. Execute — request dikirim ke server sungguhan, lalu respons penuh (status, headers, body) ditampilkan di bawah blok operasi.

Fitur ini menyelamatkan Anda dari alat seperti Postman untuk kasus sederhana: konfigurasi sudah tersedia, skema sudah diisi, token cukup sekali dimasukkan. Tidak perlu menangkap screenshot — coba sendiri di aplikasi Anda dan rasakan bedanya.

Tip: Swagger UI memanggil server sungguhan. Fitur ini memudahkan testing, tetapi pastikan Anda mengujinya pada lingkungan development/staging, bukan data produksi yang sensitif — terutama untuk operasi POST/DELETE.

OpenAPI sebagai Kontrak Tim

Ketika spesifikasi OpenAPI sudah akurat, ia menjadi lebih dari sekadar dokumentasi — ia menjadi kontrak yang bisa diprogram oleh kedua sisi.

Generate Client untuk Frontend

Karena spesifikasi OpenAPI adalah file terstruktur (JSON/YAML), frontend tidak perlu menulis kode HTTP dengan tangan. Gunakan OpenAPI Generator untuk membuat client library langsung dari spesifikasi:

# contoh: generate client TypeScript (fetch / axios)
npx @openapitools/openapi-generator-cli generate \
  -i /path/to/openapi.json \
  -g typescript-axios \
  -o src/api/client

Dari satu file spesifikasi, generator bisa memproduksi client dalam banyak bahasa (typescript-axios, typescript-fetch, dart, kotlin, dan lain-lain). Alternatif yang populer di ekosistem Angular adalah ng-openapi-gen. Keuntungannya: jenis, interface, dan fungsi pemanggil selalu sinkron dengan spesifikasi — karena mereka memang dihasilkan dari spesifikasi tersebut, bukan ditulis tangan lalu diharapkan cocok.

Contract-First vs Code-First

Ada dua cara memproduksi spesifikasi OpenAPI:

AspekContract-firstCode-first
AlurSpesifikasi ditulis dulu, lalu kode mengikutinyaKode ditulis dulu, spesifikasi dihasilkan dari kode
Contoh toolingswagger-editor, openapi-generator untuk stub serverspringdoc-openapi (otomatis)
KelebihanKontrak bisa didiskusikan dengan klien sebelum implementasi; klien bisa mulai kerja paralelTanpa usaha ekstra, dokumentasi selalu sinkron dengan kode
KekuranganButuh disiplin menjaga kode sesuai spesifikasi; bisa ketinggalan dari kodeKualitas dokumentasi bergantung pada kualitas anotasi; kontrak "baru ada" setelah kode jadi
Cocok untukTim besar, API publik/antar-tim, fitur baru yang didesain duluPrototipe, API internal, iterasi cepat

springdoc termasuk pendekatan code-first: spesifikasi di-generate dari kode. Artikel ini fokus ke situ itu. Untuk API publik yang besar, pertimbangkan contract-first agar spesifikasi bisa disetujui klien sebelum implementasi dimulai.

Verifikasi di CI (Opsional)

Ada celah kecil pada code-first: spesifikasi selalu dihasilkan ulang dari kode, jadi secara definisi sinkron. Namun pada contract-first atau saat ada klien yang bergantung pada file spesifikasi yang di-commit, tambahkan langkah di CI untuk memastikan file tersebut tidak usang:

# contoh job GitHub Actions (ringkas)
- name: Regenerate dan bandingkan spesifikasi
  run: |
    mvn springdoc-openapi-maven-plugin:generate
    git diff --exit-code -- src/main/resources/static/openapi.json

Perintah git diff --exit-code membuat build gagal bila file openapi.json berubah (berarti kode baru tanpa pembaruan kontrak). Ini menyelamatkan tim dari "spesifikasi sudah tidak sinkron dengan kode".

Keamanan Dokumentasi

Dokumentasi yang bagus adalah pedang bermata dua: ia memudahkan klien, tapi juga memudahkan penyerang memetakan permukaan serangan Anda. Perhatikan hal-hal berikut:

  1. Jangan bocorkan skema internal. Selalu tampilkan DTO, bukan entitas. Field seperti passwordHash atau detail arsitektur internal tidak pernah layak masuk ke dokumen publik.
  2. Matikan dokumentasi di produksi bila tidak diperlukan. Swagger UI diaktifkan via property Spring Boot:
springdoc.api-docs.enabled=false
springdoc.swagger-ui.enabled=false

Nyalakan hanya di profil tertentu, misalnya di dalam blok profil dev pada application.yml — sehingga dokumentasi tetap hidup untuk developer, tapi mati untuk publik produksi.

  1. Autentikasi Swagger UI. Bila dokumentasi harus ada di produksi (misal untuk operasional internal), lindungi halamannya. Karena Swagger UI adalah servlet biasa, ia bisa dibatasi lewat konfigurasi SecurityFilterChain Spring Security:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .securityMatcher("/swagger-ui/**", "/v3/api-docs/**")
        .authorizeHttpRequests(auth -> auth.anyRequest().hasRole("ADMIN"));
    return http.build();
}

Pendekatan ini memastikan hanya pengguna dengan peran ADMIN yang bisa melihat spesifikasi dan mencoba endpoint dari UI. Lebih detail tentang autentikasi ini dibahas di Session vs JWT dan RBAC Authorization.

Jebakan Umum (Common Pitfalls)

JebakanDampakSolusi
Mengekspos entitas JPA langsungBocor field internal (passwordHash) + LazyInitializationException saat serialisasiSelalu balas lewat DTO
@Operation/@ApiResponse berlebihan di tiap methodDokumen membanjiri pembaca dengan detail; summary kehilangan maknaAnotasi pada yang memang butuh konteks; biarkan yang sudah jelas dari nama method
Lupa mengganti contoh schema yang dipakai anotasiKlien melihat struktur lama yang keliruPastikan @Schema(implementation = ...) merujuk DTO terbaru
Versi spesifikasi tidak konsisten antar modulopenapi: 3.0.3 di satu file, 3.1.0 di file lain — hasil generate berubah perilakuSamakan versi openapi dan versi springdoc di seluruh modul
Dokumentasi aktif di produksiMembantu penyerang memetakan endpointMatikan atau lindungi via Spring Security

Best Practice

Ringkasan kebiasaan yang menghasilkan dokumentasi yang benar-benar dipakai orang:

  1. Deskripsi dalam bahasa tim. Tulis summary/description dengan kalimat yang dipahami rekan satu tim dan klien, bukan jargon internal. Deskripsi yang ambigu justru menimbulkan pertanyaan baru.
  2. Grouping dengan @Tag. Kelompokkan endpoint berdasarkan domain (Orders, Customers, Payments), bukan berdasarkan class Java.
  3. Dokumentasikan error response. Endpoint yang hanya mendokumentasikan 200 memberi kesan seolah tak pernah gagal. Cantumkan 400, 401, 403, 404 yang memang bisa terjadi — konsisten dengan klasifikasi status code di artikel Anatomi HTTP.
  4. Contoh nilai untuk parameter. Isi example pada parameter query/path dan @Schema(example = "...") pada field DTO. Contoh nyata jauh lebih membantu daripada tipe data kosong.
  5. Gunakan DTO, bukan entitas. Ini berulang kali disebut karena ini akar dari dokumentasi yang aman dan akurat.

Ringkasan

Dokumentasi API yang baik bukan dokumen terpisah yang cepat basi, melainkan spesifikasi OpenAPI yang di-generate otomatis dari kode dan ditampilkan lewat Swagger UI. Dengan satu dependency springdoc-openapi-starter-webmvc-ui, aplikasi Spring Boot langsung mendapat /v3/api-docs dan /swagger-ui.html; @Operation, @ApiResponse, dan @Tag membuat dokumen itu rapi, sedangkan penggunaan DTO (bukan entitas) menjaganya tetap aman. Spesifikasi yang dihasilkan bisa sekaligus berperan sebagai kontrak tim — untuk generate client frontend, sebagai dasar diskusi contract-first, dan untuk verifikasi di CI. Ingat jebakannya: ekspos DTO, dokumentasikan error, isi contoh nilai, dan jangan biarkan dokumentasi terbuka tanpa pengaman di produksi.

Lanjut membaca

← Back to technical articles