M
Mr Sugiarto
Developer
10 Oct 2026 4 min read

Di Part 1 kita sudah kenalan konsep spec-first. Sekarang kita bedah strukturnya: apa saja bagian wajib sebuah file OpenAPI, dan apa fungsi masing-masing. Sebagai contoh kerja, kita pakai notes-api.yaml — spec untuk API catatan sederhana yang akan kita generate jadi kode Go di Part 4.

Struktur Besar

Sebuah file OpenAPI 3.0 punya 4 bagian utama:

openapi: 3.0.3     # versi spesifikasi OpenAPI yang dipakai
info: ...           # metadata: judul, deskripsi, versi API
paths: ...          # daftar endpoint dan operasinya
components: ...     # skema/schema yang dipakai berulang (reusable)

info — Metadata API

info:
  title: Notes API
  description: >
    Contoh API catatan (notes) sederhana untuk belajar OpenAPI spec-first.
    Dibangun pelan-pelan di seri "OpenAPI Spec-First untuk Go Developer".
  version: 1.0.0
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

version di sini adalah versi API-nya sendiri (bukan versi file OpenAPI), berguna kalau nanti ada breaking change dan butuh versi baru (v2).

servers dan security

servers:
  - url: http://localhost:8080
    description: Local development server

security: []

servers mendaftar base URL yang valid untuk API ini — tools seperti Swagger UI pakai ini untuk tahu ke mana request dikirim saat dicoba langsung dari browser.

security: [] di level root berarti secara eksplisit API ini tidak butuh autentikasi apa pun. Ini penting: linter OpenAPI (akan kita pakai di Part 5) akan komplain kalau security tidak didefinisikan sama sekali di suatu operation — karena nggak jelas apakah itu memang publik atau cuma lupa ditulis. Eksplisit [] menghilangkan ambiguitas itu.

paths — Endpoint dan Operasinya

Ini bagian paling besar. Satu path bisa punya beberapa method (get, post, put, delete), dan tiap method adalah satu operation:

paths:
  /notes:
    get:
      operationId: listNotes
      tags: [Notes]
      summary: Daftar notes dengan pagination
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 10
      responses:
        '200':
          description: Daftar notes berhasil diambil
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Note'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '400':
          $ref: '#/components/responses/BadRequest'

Beberapa hal penting di sini:

  • operationId — nama unik untuk operation ini. Nanti ini yang jadi nama fungsi Go saat digenerate (listNotes → ListNotes). Wajib unik di seluruh file.
  • parameters dengan in: query — parameter lewat query string (?page=1&limit=10). Ada juga in: path (lihat /notes/{id} di bawah) dan in: header.
  • responses — key-nya adalah HTTP status code. $ref: '#/components/responses/BadRequest' memakai ulang definisi response yang didefinisikan sekali di components, supaya tidak copy-paste di setiap endpoint yang bisa mengembalikan 400.

Untuk endpoint yang punya path parameter, parameter itu bisa didefinisikan sekali di level path (berlaku untuk semua method di bawahnya):

  /notes/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      operationId: getNote
      tags: [Notes]
      summary: Detail 1 note
      responses:
        '200':
          description: Note ditemukan
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NoteResponse'
        '404':
          $ref: '#/components/responses/NotFound'

Untuk post/put, ada tambahan requestBody yang mendeskripsikan bentuk JSON body yang diterima:

    post:
      operationId: createNote
      tags: [Notes]
      summary: Buat note baru
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateNoteRequest'
      responses:
        '201':
          description: Note berhasil dibuat
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NoteResponse'
        '400':
          $ref: '#/components/responses/BadRequest'

components/schemas — Skema yang Dipakai Berulang

Ini "kamus" tipe data yang dirujuk lewat $ref di mana saja dalam file. Keuntungannya: definisi Note cukup ditulis sekali, dipakai di banyak response.

components:
  schemas:
    Note:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        body:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required: [id, title, body, created_at, updated_at]

    CreateNoteRequest:
      type: object
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 255
        body:
          type: string
          minLength: 1
      required: [title, body]

    NoteResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        data:
          $ref: '#/components/schemas/Note'

    PaginationMeta:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer

    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        message:
          type: string
        error:
          type: string

Perhatikan minLength, maxLength, minimum, maximum — ini bukan cuma dokumentasi, tapi constraint yang nanti bisa divalidasi otomatis oleh tooling (dan jadi dokumentasi yang jelas buat siapa pun yang baca spec ini).

components/responses menyimpan response yang dipakai berulang di banyak endpoint (400 dan 404 di sini):

  responses:
    BadRequest:
      description: Request tidak valid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource tidak ditemukan
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

File Lengkapnya

Setelah semua bagian di atas digabung, api/openapi/notes-api.yaml lengkapnya mendeskripsikan 5 operation: listNotes, createNote, getNote, updateNote, deleteNote — lengkap dengan request, response sukses, dan response error untuk tiap satu.

Spec ini belum menghasilkan satu baris kode Go pun. Di Part 3, kita kenalan dengan ekosistem tools yang akan mengubah file YAML ini jadi types dan interface Go — sebelum di Part 4 kita benar-benar menjalankan generate-nya.

M
Mr Sugiarto

Developer

Bagian dari Series: OpenAPI Spec-First untuk Go Developer

Seri 8 bagian: belajar mendesain REST API dengan pendekatan spec-first di Go - menulis kontrak OpenAPI dulu, generate kode, lalu terapkan ke project h...

Lihat Series Lengkap