Web Development

REST API Design: Membuat API yang Baik

Panduan komprehensif mendesain REST API yang profesional — prinsip REST, HTTP methods, status codes, URL naming, versioning, pagination, autentikasi, error handling, dan dokumentasi

1. Prinsip-prinsip REST

REST (Representational State Transfer) adalah arsitektur software yang diperkenalkan oleh Roy Fielding dalam disertasinya tahun 2000. REST bukan standar, melainkan sekumpulan prinsip desain yang digunakan untuk membangun web services yang scalable, maintainable, dan interoperable.

API yang mengikuti prinsip REST disebut RESTful API. Mayoritas API publik saat ini — termasuk Twitter, GitHub, Google Maps, dan Stripe — menggunakan arsitektur REST karena kesederhanaan dan skalabilitasnya.

6 Prinsip Utama REST

Prinsip Penjelasan Contoh Implementasi
Client-ServerClient dan server terpisah — masing-masing bisa berkembang secara independenFrontend React terpisah dari backend Node.js
StatelessSetiap request harus berisi semua informasi yang dibutuhkan server — tidak ada session stateToken JWT dikirim di setiap request header
CacheableResponse harus menyatakan apakah data bisa di-cache atau tidakHeader Cache-Control, ETag
Uniform InterfaceAntarmuka yang konsisten menggunakan URL dan HTTP methods standarGET /users, POST /users
Layered SystemClient tidak perlu tahu apakah berbicara langsung dengan server atau melalui proxy/CDNAPI Gateway, load balancer, CDN
Code on Demand (opsional)Server bisa mengirim executable code ke clientJavaScript di response (jarang digunakan)
Diagram: Arsitektur REST API
┌───────────────────────────────────────────────────────────┐
│                    REST API ARCHITECTURE                   │
│                                                           │
│  ┌──────────┐         ┌───────────────────────────────┐   │
│  │  Client   │         │         REST API Server       │   │
│  │          │         │                               │   │
│  │ Browser  │  HTTP   │  ┌──────────┐ ┌───────────┐  │   │
│  │ Mobile   │◄───────►│  │  Router  │ │ Middleware │  │   │
│  │ CLI      │ Request │  └────┬─────┘ └─────┬─────┘  │   │
│  │          │         │       │             │         │   │
│  └──────────┘         │  ┌────▼─────────────▼─────┐  │   │
│                       │  │     Controllers         │  │   │
│                       │  │  (Business Logic)       │  │   │
│                       │  └────────────┬────────────┘  │   │
│                       │               │               │   │
│                       │  ┌────────────▼────────────┐  │   │
│                       │  │     Database Layer       │  │   │
│                       │  │  (PostgreSQL/MongoDB)    │  │   │
│                       │  └─────────────────────────┘  │   │
│                       └───────────────────────────────┘   │
└───────────────────────────────────────────────────────────┘
💡 REST vs GraphQL vs gRPC

REST bukan satu-satunya pilihan untuk API. GraphQL cocok untuk data yang kompleks dan butuh query fleksibel. gRPC cocok untuk komunikasi antar-microservice berperforma tinggi. Namun, REST tetap pilihan terbaik untuk sebagian besar kasus karena kesederhanaan, dokumentasi luas, dan kompatibilitas universal.

2. HTTP Methods

HTTP methods (juga disebut HTTP verbs) mendefinisikan operasi apa yang ingin dilakukan client terhadap resource. Dalam REST API, setiap operasi CRUD dipetakan ke HTTP method yang sesuai.

Mapping HTTP Methods ke Operasi CRUD

HTTP Method Operasi URL Contoh Berhasil Aman? Idempoten?
GETRead (ambil data)GET /api/users200 OK✅ Ya✅ Ya
POSTCreate (buat baru)POST /api/users201 Created❌ Tidak❌ Tidak
PUTUpdate (ganti seluruh)PUT /api/users/1200 OK❌ Tidak✅ Ya
PATCHUpdate (sebagian)PATCH /api/users/1200 OK❌ Tidak❌ Tidak
DELETEDelete (hapus)DELETE /api/users/1204 No Content❌ Tidak✅ Ya
HEADSeperti GET tanpa bodyHEAD /api/users200 OK✅ Ya✅ Ya
OPTIONSInfo methods yang tersediaOPTIONS /api/users200 OK✅ Ya✅ Ya
📖 Istilah Penting
  • Aman (Safe): Method tidak mengubah data di server — hanya membaca.
  • Idempoten: Memanggil method yang sama berkali-kali menghasilkan hasil yang sama. Misalnya DELETE /users/1 — meskipun dipanggil 5 kali, hasilnya tetap: user 1 terhapus.
  • POST tidak idempoten: Memanggil POST /users berkali-kali akan membuat banyak user baru.

Contoh Penggunaan

HTTP — Contoh Request & Response
# ── GET: Mengambil daftar pengguna ──
GET /api/v1/users?page=1&limit=10 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

# Response:
# HTTP/1.1 200 OK
# Content-Type: application/json
{
  "status": "success",
  "data": [
    { "id": 1, "name": "Budi", "email": "budi@mail.com" },
    { "id": 2, "name": "Sari", "email": "sari@mail.com" }
  ],
  "pagination": { "page": 1, "limit": 10, "total": 2 }
}


# ── POST: Membuat pengguna baru ──
POST /api/v1/users HTTP/1.1
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

{
  "name": "Andi Pratama",
  "email": "andi@mail.com",
  "password": "rahasia123"
}

# Response:
# HTTP/1.1 201 Created
# Location: /api/v1/users/3
{ "status": "success", "data": { "id": 3, "name": "Andi Pratama", "email": "andi@mail.com" } }


# ── PUT: Mengganti seluruh data pengguna ──
PUT /api/v1/users/3 HTTP/1.1
Content-Type: application/json

{
  "name": "Andi Pratama",
  "email": "andi.baru@mail.com",
  "password": "passwordBaru456"
}


# ┶ PATCH: Mengubah sebagian data ──
PATCH /api/v1/users/3 HTTP/1.1
Content-Type: application/json

{ "email": "andi.update@mail.com" }


# ── DELETE: Menghapus pengguna ──
DELETE /api/v1/users/3 HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

# Response:
# HTTP/1.1 204 No Content

3. HTTP Status Codes

HTTP status code memberitahu client hasil dari request-nya. Menggunakan status code yang tepat adalah bagian penting dari desain REST API yang baik. Status code dibagi menjadi 5 kategori berdasarkan digit pertama.

Kategori Status Code

Kategori Range Arti
🟢 Informational1xxRequest diterima, proses berlanjut
🟢 Success2xxRequest berhasil diproses
🟡 Redirection3xxPerlu redirect ke URL lain
🔴 Client Error4xxKesalahan dari sisi client
🔴 Server Error5xxKesalahan dari sisi server

Status Code yang Sering Digunakan dalam REST API

Code Nama Kapan Digunakan
200OKRequest berhasil — GET, PUT, PATCH
201CreatedResource baru berhasil dibuat — POST
204No ContentRequest berhasil, tidak ada body response — DELETE
301Moved PermanentlyResource telah pindah secara permanen
400Bad RequestData request tidak valid (format salah, field kurang)
401UnauthorizedClient belum terotentikasi (tidak punya token)
403ForbiddenClient terotentikasi tapi tidak punya izin
404Not FoundResource tidak ditemukan
405Method Not AllowedHTTP method tidak didukung untuk URL ini
409ConflictKonflik — misalnya email sudah terdaftar
422Unprocessable EntityFormat benar tapi data tidak bisa diproses
429Too Many RequestsRate limit terlampaui
500Internal Server ErrorError tak terduga di server
502Bad GatewayServer upstream mengembalikan response tidak valid
503Service UnavailableServer sedang maintenance atau overload
⚠️ Jangan Salahgunakan Status Code!

Kesalahan umum: mengembalikan 200 OK untuk semua response termasuk error, lalu menandai error di dalam body JSON. Ini melanggar konvensi REST. Gunakan status code yang tepat sehingga client bisa memproses response secara otomatis.

4. URL Design & Naming Conventions

Desain URL yang baik membuat API intuitif dan mudah digunakan. Ada beberapa konvensi yang harus diikuti agar API Anda konsisten dan profesional.

Konvensi URL Naming

Aturan ✅ Benar ❌ Salah
Gunakan noun (kata benda), bukan verbGET /api/usersGET /api/getUsers
Gunakan plural untuk collection/api/articles/api/article
Gunakan lowercase dan hyphens/api/user-profiles/api/UserProfiles
Hindari file extension/api/users/1/api/users/1.json
Hindari trailing slash/api/users/api/users/
Gunakan nesting untuk relasi/api/users/1/orders/api/userOrders/1
Jangan terlalu dalam (max 3 level)/api/users/1/orders/5/api/users/1/orders/5/items/3

Contoh Desain URL yang Baik

URL Design — Contoh
# ── Resource Collection ──
GET    /api/v1/users              # Ambil semua user
POST   /api/v1/users              # Buat user baru

# ── Single Resource ──
GET    /api/v1/users/42           # Ambil user ID 42
PUT    /api/v1/users/42           # Update user 42 (full)
PATCH  /api/v1/users/42           # Update user 42 (partial)
DELETE /api/v1/users/42           # Hapus user 42

# ── Nested Resources (relasi) ──
GET    /api/v1/users/42/orders    # Semua order milik user 42
POST   /api/v1/users/42/orders    # Buat order baru untuk user 42
GET    /api/v1/users/42/orders/7  # Order 7 milik user 42

# ── Sub-resource Operations ──
POST   /api/v1/articles/5/comments    # Tambah komentar pada artikel 5
GET    /api/v1/articles/5/comments    # Ambil semua komentar artikel 5

# ── Actions (ketika CRUD tidak cukup) ──
POST   /api/v1/users/42/activate     # Aktifkan user 42
POST   /api/v1/orders/7/cancel       # Batalkan order 7
POST   /api/v1/articles/5/publish    # Publikasikan artikel 5

# ── Search & Filter ──
GET    /api/v1/articles?category=teknologi&sort=-created_at
GET    /api/v1/users?search=budi&role=admin&page=2

# ── WRONG: Contoh URL yang buruk ──
# GET /api/getUser              ← menggunakan verb
# GET /api/user                 ← singular
# GET /api/all-users            ← prefix tidak perlu
# POST /api/users/create        ← verb di URL
# GET /api/Users/Posts          ← PascalCase

5. API Versioning

API versioning memungkinkan Anda merilis perubahan yang breaking tanpa merusak client yang sudah ada. Setiap versi API berjalan secara paralel sehingga client bisa migrate sesuai jadwal mereka.

Strategi Versioning

Strategi Contoh Kelebihan Kekurangan
URL Path (populer) /api/v1/users Jelas, mudah di-cache, mudah di-route URL berubah antar versi
Query Parameter /api/users?version=1 URL tetap sama Kurang umum, sulit di-cache
Custom Header Api-Version: 1 URL bersih Tidak terlihat di URL, perlu dokumentasi
Content Negotiation Accept: application/vnd.api.v1+json Sesuai standar HTTP Kompleks, sulit di-debug
JavaScript — Implementasi Versioning (Express)
const express = require('express');
const app = express();
app.use(express.json());

// ═══════════════════════════════════════
// Strategi 1: URL Path Versioning
// ═══════════════════════════════════════

// API v1 — response lama (legacy format)
app.get('/api/v1/users', (req, res) => {
  res.json({
    users: [
      { id: 1, name: 'Budi', email: 'budi@mail.com' }
    ]
  });
});

// API v2 — response baru (dengan metadata dan pagination)
app.get('/api/v2/users', (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;

  res.json({
    status: 'success',
    meta: {
      version: '2.0',
      timestamp: new Date().toISOString()
    },
    data: {
      items: [
        { id: 1, nama_lengkap: 'Budi Santoso', email: 'budi@mail.com' }
      ],
      pagination: {
        halaman: page,
        per_halaman: limit,
        total: 1,
        total_halaman: 1
      }
    }
  });
});

// ═══════════════════════════════════════
// Strategi 2: Router Modular per Versi
// ═══════════════════════════════════════
const userRoutesV1 = require('./routes/v1/users');
const userRoutesV2 = require('./routes/v2/users');

app.use('/api/v1/users', userRoutesV1);
app.use('/api/v2/users', userRoutesV2);

// ═══════════════════════════════════════
// Header versioning (untuk API internal)
// ═══════════════════════════════════════
app.use((req, res, next) => {
  const version = req.headers['api-version'] || 'v1';
  req.apiVersion = version;
  next();
});

6. Pagination, Filtering & Sorting

Ketika API mengembalikan data dalam jumlah besar, pagination (paginasi) sangat penting untuk menjaga performa dan menghemat bandwidth. Selain pagination, filtering dan sorting memungkinkan client mengambil data yang tepat sesuai kebutuhan.

Strategi Pagination

Strategi Parameter Cocok Untuk Contoh
Offset-basedpage & limitData statis, admin panel?page=2&limit=10
Cursor-basedcursor & limitData real-time, infinite scroll?cursor=eyJpZCI6MTB9&limit=10
JavaScript — Pagination, Filtering & Sorting
// ═══════════════════════════════════════
// Endpoint dengan fitur lengkap
// GET /api/v1/articles?category=tech&sort=-created_at&page=1&limit=10
// ═══════════════════════════════════════

app.get('/api/v1/articles', (req, res) => {
  let hasil = [...artikelDB];

  // ── Filtering ──
  if (req.query.category) {
    hasil = hasil.filter(a => a.category === req.query.category);
  }
  if (req.query.author) {
    hasil = hasil.filter(a =>
      a.author.toLowerCase().includes(req.query.author.toLowerCase())
    );
  }
  if (req.query.status) {
    hasil = hasil.filter(a => a.status === req.query.status);
  }
  if (req.query.search) {
    const keyword = req.query.search.toLowerCase();
    hasil = hasil.filter(a =>
      a.title.toLowerCase().includes(keyword) ||
      a.body.toLowerCase().includes(keyword)
    );
  }

  // ── Sorting (prefix "-" untuk descending) ──
  const sortField = (req.query.sort || 'created_at').replace('-', '');
  const sortOrder = req.query.sort?.startsWith('-') ? -1 : 1;

  hasil.sort((a, b) => {
    if (a[sortField] < b[sortField]) return -1 * sortOrder;
    if (a[sortField] > b[sortField]) return 1 * sortOrder;
    return 0;
  });

  // ── Pagination ──
  const halaman = parseInt(req.query.page) || 1;
  const limit = Math.min(parseInt(req.query.limit) || 10, 100); // max 100
  const totalData = hasil.length;
  const totalHalaman = Math.ceil(totalData / limit);
  const mulai = (halaman - 1) * limit;

  hasil = hasil.slice(mulai, mulai + limit);

  // ── Response dengan metadata ──
  res.json({
    status: 'success',
    data: hasil,
    pagination: {
      halaman: halaman,
      per_halaman: limit,
      total_data: totalData,
      total_halaman: totalHalaman,
      halaman_berikutnya: halaman < totalHalaman ? halaman + 1 : null,
      halaman_sebelumnya: halaman > 1 ? halaman - 1 : null
    },
    links: {
      self: `/api/v1/articles?page=${halaman}&limit=${limit}`,
      first: `/api/v1/articles?page=1&limit=${limit}`,
      last: `/api/v1/articles?page=${totalHalaman}&limit=${limit}`,
      next: halaman < totalHalaman
        ? `/api/v1/articles?page=${halaman + 1}&limit=${limit}` : null,
      prev: halaman > 1
        ? `/api/v1/articles?page=${halaman - 1}&limit=${limit}` : null
    }
  });
});

7. Autentikasi & Otorisasi

Autentikasi memverifikasi identitas client (siapa Anda?), sedangkan otorisasi menentukan hak akses (apa yang boleh Anda lakukan?). Dua metode autentikasi yang paling umum untuk REST API adalah API Key dan JWT (JSON Web Token).

Perbandingan Metode Autentikasi

Metode Cara Kerja Cocok Untuk Keamanan
API KeyKey dikirim di header atau queryAPI publik, integrasi pihak ketiga🟡 Sedang
JWT (Bearer Token)Token dikirim di header AuthorizationWeb app, mobile app, SPA🟢 Tinggi
OAuth 2.0Alur authorization codeLogin social, ekosistem besar🟢 Tinggi
Session CookieCookie dengan session IDServer-rendered web app🟢 Tinggi
JavaScript — JWT Authentication
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

const JWT_SECRET = process.env.JWT_SECRET || 'rahasia-super-aman';
const JWT_EXPIRES = '24h';

// ═══════════════════════════════════════
// Endpoint: Register
// POST /api/v1/auth/register
// ═══════════════════════════════════════
app.post('/api/v1/auth/register', async (req, res) => {
  const { nama, email, password } = req.body;

  // Validasi input
  if (!nama || !email || !password) {
    return res.status(400).json({
      status: 'error',
      message: 'Nama, email, dan password wajib diisi'
    });
  }

  // Cek apakah email sudah terdaftar
  const existing = users.find(u => u.email === email);
  if (existing) {
    return res.status(409).json({
      status: 'error',
      message: 'Email sudah terdaftar'
    });
  }

  // Hash password
  const hashedPassword = await bcrypt.hash(password, 12);

  // Simpan user
  const newUser = {
    id: users.length + 1,
    nama,
    email,
    password: hashedPassword,
    role: 'user',
    created_at: new Date().toISOString()
  };
  users.push(newUser);

  // Buat JWT token
  const token = jwt.sign(
    { id: newUser.id, email: newUser.email, role: newUser.role },
    JWT_SECRET,
    { expiresIn: JWT_EXPIRES }
  );

  res.status(201).json({
    status: 'success',
    data: {
      user: { id: newUser.id, nama, email },
      token: token
    }
  });
});

// ═══════════════════════════════════════
// Endpoint: Login
// POST /api/v1/auth/login
// ═══════════════════════════════════════
app.post('/api/v1/auth/login', async (req, res) => {
  const { email, password } = req.body;

  const user = users.find(u => u.email === email);
  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({
      status: 'error',
      message: 'Email atau password salah'
    });
  }

  const token = jwt.sign(
    { id: user.id, email: user.email, role: user.role },
    JWT_SECRET,
    { expiresIn: JWT_EXPIRES }
  );

  res.json({
    status: 'success',
    data: {
      user: { id: user.id, nama: user.nama, email: user.email },
      token: token
    }
  });
});

// ═══════════════════════════════════════
// Middleware: Verifikasi Token
// ═══════════════════════════════════════
const autentikasi = (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({
      status: 'error',
      message: 'Token tidak ditemukan. Silakan login.'
    });
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({
      status: 'error',
      message: 'Token tidak valid atau sudah kedaluwarsa'
    });
  }
};

// Middleware: Cek Role
const izinkanRole = (...roles) => {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({
        status: 'error',
        message: 'Anda tidak memiliki akses ke resource ini'
      });
    }
    next();
  };
};

// ═══════════════════════════════════════
// Protected Routes
// ═══════════════════════════════════════
app.get('/api/v1/profile', autentikasi, (req, res) => {
  res.json({ status: 'success', user: req.user });
});

// Hanya admin yang bisa menghapus
app.delete('/api/v1/users/:id',
  autentikasi,
  izinkanRole('admin'),
  (req, res) => {
    // Hapus user...
    res.json({ status: 'success', message: 'User dihapus' });
  }
);

8. Error Handling yang Baik

Response error yang konsisten dan informatif sangat penting agar client dapat menangani error dengan baik. Format error yang terstruktur akan menghemat waktu developer yang menggunakan API Anda.

Format Error Response yang Baik

JSON — Format Error Response
// ✅ Format error yang BAIK (terstruktur & konsisten)
{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Data yang dikirim tidak valid",
    "details": [
      {
        "field": "email",
        "message": "Format email tidak valid",
        "value": "budi-at-mail.com"
      },
      {
        "field": "password",
        "message": "Password minimal 8 karakter",
        "value": "****"
      }
    ]
  },
  "meta": {
    "timestamp": "2026-06-25T10:30:00Z",
    "request_id": "req_abc123xyz"
  }
}

// ── Contoh error berbagai skenario ──

// 400 Bad Request
{
  "status": "error",
  "error": {
    "code": "BAD_REQUEST",
    "message": "Request body tidak valid. Pastikan Content-Type: application/json"
  }
}

// 401 Unauthorized
{
  "status": "error",
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Token autentikasi tidak ditemukan atau tidak valid"
  }
}

// 403 Forbidden
{
  "status": "error",
  "error": {
    "code": "FORBIDDEN",
    "message": "Anda tidak memiliki izin untuk mengakses resource ini"
  }
}

// 404 Not Found
{
  "status": "error",
  "error": {
    "code": "NOT_FOUND",
    "message": "Resource dengan ID 42 tidak ditemukan"
  }
}

// 409 Conflict
{
  "status": "error",
  "error": {
    "code": "CONFLICT",
    "message": "Email 'budi@mail.com' sudah terdaftar"
  }
}

// 429 Too Many Requests
{
  "status": "error",
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Terlalu banyak request. Coba lagi dalam 60 detik.",
    "retry_after": 60
  }
}

Global Error Handler

JavaScript — Global Error Handler
// Global error handler middleware (Express)
app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  const errorCode = err.code || 'INTERNAL_ERROR';

  // Log error untuk developer
  console.error({
    timestamp: new Date().toISOString(),
    method: req.method,
    url: req.url,
    status: statusCode,
    message: err.message,
    stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,
    requestId: req.requestId
  });

  // Response untuk client
  res.status(statusCode).json({
    status: 'error',
    error: {
      code: errorCode,
      message: statusCode === 500
        ? 'Terjadi kesalahan internal. Silakan coba lagi nanti.'
        : err.message
    },
    meta: {
      timestamp: new Date().toISOString(),
      request_id: req.requestId
    }
  });
});

9. Dokumentasi API (OpenAPI/Swagger)

Dokumentasi yang baik adalah kunci keberhasilan API. Tanpa dokumentasi, developer lain tidak akan tahu cara menggunakan API Anda. Standar OpenAPI Specification (sebelumnya Swagger) adalah format paling populer untuk mendokumentasikan REST API.

Manfaat Dokumentasi API

Manfaat Penjelasan
Mempercepat OnboardingDeveloper baru bisa langsung mulai menggunakan API tanpa bertanya
Self-serviceDeveloper bisa mencoba API langsung dari dokumentasi
Client SDK GenerationTools seperti openapi-generator bisa membuat SDK otomatis
TestingContract testing bisa dilakukan dari spesifikasi OpenAPI
KonsistensiDokumentasi menjadi "kontrak" antara frontend dan backend team

Contoh Spesifikasi OpenAPI

YAML — openapi.yaml
openapi: 3.0.3
info:
  title: BeebaneLabs API
  description: API untuk platform tutorial BeebaneLabs
  version: 1.0.0
  contact:
    name: BeebaneLabs
    url: https://beebanelabs.pages.dev
  license:
    name: MIT

servers:
  - url: https://api.beebanelabs.dev/v1
    description: Production
  - url: http://localhost:3000/v1
    description: Development

paths:
  /articles:
    get:
      summary: Mendapatkan daftar artikel
      description: Mengembalikan daftar artikel dengan pagination, filter, dan sorting
      tags: [Articles]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
          description: Nomor halaman
        - name: limit
          in: query
          schema:
            type: integer
            default: 10
            maximum: 100
          description: Jumlah item per halaman
        - name: category
          in: query
          schema:
            type: string
          description: Filter berdasarkan kategori
        - name: sort
          in: query
          schema:
            type: string
            default: "-created_at"
          description: "Sorting (prefix - untuk descending)"
      responses:
        '200':
          description: Berhasil mengambil daftar artikel
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Article'
                  pagination:
                    $ref: '#/components/schemas/Pagination'

    post:
      summary: Membuat artikel baru
      tags: [Articles]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title, body]
              properties:
                title:
                  type: string
                  example: "Tutorial REST API"
                body:
                  type: string
                  example: "Isi artikel di sini..."
                category:
                  type: string
                  example: "web-development"
      responses:
        '201':
          description: Artikel berhasil dibuat
        '400':
          description: Data tidak valid
        '401':
          description: Tidak terotentikasi

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  schemas:
    Article:
      type: object
      properties:
        id:
          type: integer
          example: 1
        title:
          type: string
          example: "Tutorial REST API"
        body:
          type: string
        category:
          type: string
        author:
          type: string
        created_at:
          type: string
          format: date-time

    Pagination:
      type: object
      properties:
        halaman:
          type: integer
          example: 1
        per_halaman:
          type: integer
          example: 10
        total_data:
          type: integer
          example: 42
        total_halaman:
          type: integer
          example: 5
💡 Tools Dokumentasi API
  • Swagger UI — Dokumentasi interaktif berbasis browser (paling populer)
  • Redoc — Dokumentasi dengan tampilan lebih elegan dan responsif
  • Postman — Buat dan bagikan dokumentasi langsung dari collection
  • Stoplight Studio — Editor visual untuk OpenAPI Specification
  • Scalar — Alternatif modern untuk Swagger UI dengan UI lebih baik

10. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang REST API Design:

Pertanyaan 1: Prinsip REST mana yang menyatakan bahwa setiap request harus berisi semua informasi yang dibutuhkan server?

a) Client-Server
b) Stateless
c) Cacheable
d) Uniform Interface

Pertanyaan 2: HTTP status code mana yang tepat untuk response ketika resource baru berhasil dibuat melalui POST?

a) 200 OK
b) 201 Created
c) 204 No Content
d) 301 Moved Permanently

Pertanyaan 3: Manakah URL yang paling sesuai dengan konvensi REST API design?

a) GET /api/getAllUsers
b) GET /api/user
c) GET /api/v1/users
d) GET /api/Users/list

Pertanyaan 4: Apa perbedaan antara HTTP method PUT dan PATCH?

a) Tidak ada perbedaan
b) PUT untuk membuat, PATCH untuk menghapus
c) PUT mengganti seluruh resource, PATCH mengubah sebagian
d) PUT lebih cepat dari PATCH

Pertanyaan 5: HTTP status code 401 dan 403 memiliki perbedaan penting. Apa arti masing-masing?

a) 401 = Server error, 403 = Client error
b) 401 = Tidak terotentikasi (belum login), 403 = Tidak terotorisasi (tidak punya izin)
c) 401 = Resource tidak ditemukan, 403 = Method tidak diizinkan
d) 401 dan 403 artinya sama
🔍 Zoom
100%
🎨 Tema