API yang tidak terdokumentasi adalah API yang hanya bisa dipakai pembuatnya: calon pengguna tidak tahu endpoint apa yang tersedia, field apa yang wajib dikirim, atau bentuk error seperti apa yang harus diantisipasi. Pada Bagian 7 — bagian penutup proyek — kita menuangkan seluruh kontrak API ke dalam berkas openapi.yaml berstandar OpenAPI 3.1, lalu mengemas aplikasi ke dalam Dockerfile sehingga bisa dijalankan di mesin mana pun tanpa memasang PHP secara manual. Ditutup dengan pemeriksaan kesesuaian antara dokumen dan server yang benar-benar berjalan, catatan jujur tentang kematangan server bawaan PHP, dan rekap tujuh bagian.
Kontrak API dalam openapi.yaml #
OpenAPI adalah bahasa standar untuk mendeskripsikan REST API: satu berkas YAML berisi daftar endpoint, parameter, request body, response, skema data, hingga skema keamanan. Dokumen ini bukan sekadar bacaan manusia — alat seperti Swagger UI merendernya menjadi halaman interaktif, generator klien membangkitkan kode darinya, dan reviewer memakainya sebagai acuan resmi perilaku API. Bagian pembuka berkas kita:
openapi: 3.1.0
info:
title: REST API Manajemen Tugas
summary: API JSON untuk mengelola daftar tugas.
description: |
API manajemen tugas yang dibangun dengan PHP murni tanpa framework.
Seluruh response dan request body berbentuk JSON. Endpoint /api/tugas
terlindungi bearer token; minta token lewat POST /api/login, lalu
kirim sebagai header "Authorization: Bearer <token>".
version: 1.0.0
servers:
- url: http://127.0.0.1:8097
description: Server pengembangan lokal (php -S)
- url: http://localhost:8080
description: Container DockerBlok info menjawab “API apa ini”, sedangkan servers mendaftarkan alamat dasar yang bisa dituju klien — dua lingkungan yang kita pakai: server pengembangan lokal dan container Docker yang akan dibangun sebentar lagi. Setiap operasi diberi tags (kesehatan, auth, tugas) yang kelak mengelompokkan endpoint pada tampilan Swagger UI.
Mendokumentasikan Route Terlindungi #
Delapan operasi terdokumentasi: GET /api/kesehatan, POST /api/login, POST /api/logout, serta CRUD lengkap /api/tugas dan /api/tugas/{id}. Operasi publik tidak memuat kunci security; operasi terlindungi memuatnya dan merujuk skema bearerAuth. Berikut operasi pembuatan tugas, lengkap dengan request body dan tiga kemungkinan response:
/api/tugas:
post:
tags: [tugas]
summary: Buat tugas baru
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TugasInput'
example:
judul: Belajar authentication
status: belum
responses:
'201':
description: Tugas tersimpan.
content:
application/json:
schema:
$ref: '#/components/schemas/Tugas'
example:
id: 4
judul: Belajar authentication
status: belum
dibuat_pada: '2026-10-13 22:20:56'
'401':
$ref: '#/components/responses/TidakSah'
'422':
$ref: '#/components/responses/ValidasiGagal'Tiga hal pada potongan di atas adalah inti gaya dokumentasi ini. Pertama, security: [{bearerAuth: []}] menyatakan bahwa operasi menuntut token — tampilan Swagger UI akan otomatis menyediakan kolom token, dan klien generator tahu harus menyertakan header Authorization. Kedua, setiap response membawa example yang diambil dari transkrip nyata sesi Bagian 5-6, sehingga pembaca bisa membandingkan langsung. Ketiga, response 401 dan 422 tidak ditulis ulang di tiap operasi melainkan dirujuk lewat $ref ke komponen bersama — satu tempat mengubah, semua operasi ikut. Berikut komponen response untuk penolakan token:
responses:
TidakSah:
description: Token tidak dikirim, tidak dikenal, atau kedaluwarsa.
headers:
WWW-Authenticate:
description: Selalu berisi nilai Bearer.
schema:
type: string
example: Bearer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: "Kirim token lewat header Authorization: Bearer <token>."Perhatikan bahwa header WWW-Authenticate ikut didokumentasikan — detail kecil yang kita bangun di TokenAuth::periksa() pada Bagian 5 kini menjadi janji tertulis. Komponen sejenis tersedia juga untuk TidakDitemukan (404), ValidasiGagal (422), dan MethodNotAllowed (405) beserta header Allow-nya.
Skema dan Contoh di Components #
Bagian components/schemas mendefinisikan bentuk data yang dipertukarkan. Skema Tugas menjadi rujukan semua response tugas; TugasInput dan TugasPerubahan menjadi rujukan request body:
schemas:
Tugas:
type: object
required: [id, judul, status, dibuat_pada]
properties:
id:
type: integer
example: 4
judul:
type: string
example: Belajar SQLite
status:
type: string
enum: [belum, proses, selesai]
example: belum
dibuat_pada:
type: string
description: Waktu pembuatan format YYYY-MM-DD HH:MM:SS (waktu server).
example: '2026-01-15 08:00:00'
# ... skema TugasInput, TugasPerubahan, LoginInput, Token, ErrorNilai required dan enum pada skema bukan hiasan: keduanya adalah duplikasi resmi dari aturan yang dijaga Validator (Bagian 4) dan constraint CHECK database (Bagian 3). Ketika tiga lapis itu menyebut nilai yang sama — validator, database, dokumen — kontrak terasa konsisten; ketika salah satunya melenceng, itulah bug dokumentasi yang paling sering mengecewakan pengguna. Skema Error sengaja hanya menuntut error.code dan error.message wajib, dengan details opsional — persis perilaku ErrorHandler yang menyertakan details hanya bila ada informasi tambahan.
Memeriksa Contoh JSON Secara Otomatis #
Bagaimana memastikan berkas openapi.yaml tidak berbohong? Lingkungan PHP kita tidak memuat ekstensi YAML, jadi kita tidak bisa mengurai berkasnya langsung dari PHP. Yang bisa dikerjakan adalah mengambil contoh-contoh JSON di dalamnya — yang memang paling sering disalin pengguna — dan memeriksa strukturnya terhadap aturan skema:
<?php
/**
* Pemeriksa bentuk JSON untuk contoh-contoh di openapi.yaml.
* PHP pada lingkungan ini tidak memuat ekstensi YAML, jadi contoh JSON
* disalin dari berkas spesifikasi lalu strukturnya diperiksa satu per satu.
*/
/**
* @param array<string, string> $wajib kolom wajib => tipe yang diharapkan.
* @param array<string, list<string>> $enum kolom => nilai yang diizinkan.
*/
function periksaBentuk(string $label, string $json, array $wajib, array $enum = []): void
{
if (!json_validate($json)) {
printf("%-12s: GAGAL — bukan JSON valid\n", $label);
return;
}
$data = json_decode($json, true);
foreach ($wajib as $kolom => $tipe) {
if (!array_key_exists($kolom, $data)) {
printf("%-12s: GAGAL — kolom '%s' hilang\n", $label, $kolom);
return;
}
if (gettype($data[$kolom]) !== $tipe) {
printf(
"%-12s: GAGAL — kolom '%s' bertipe %s, skema menuntut %s\n",
$label,
$kolom,
gettype($data[$kolom]),
$tipe,
);
return;
}
}
foreach ($enum as $kolom => $daftar) {
if (!in_array($data[$kolom], $daftar, true)) {
printf("%-12s: GAGAL — nilai '%s' di luar %s\n", $label, $data[$kolom], implode('/', $daftar));
return;
}
}
printf("%-12s: cocok (%d kolom wajib)\n", $label, count($wajib));
}
// Contoh-contoh berikut disalin dari openapi.yaml.
$tugas = '{"id":4,"judul":"Belajar SQLite","status":"belum","dibuat_pada":"2026-01-15 08:00:00"}';
$login = '{"email":"[email protected]","sandi":"sandi-budi-2026"}';
$token = '{"token":"79a2e78d8883fe1c4626cb26074d8c6ec1f2c3671cd7b8b322f1368d16594396"}';
$error = '{"error":{"code":"NOT_FOUND","message":"Tugas dengan id 99 tidak ditemukan."}}';
$statusValid = ['belum', 'proses', 'selesai'];
$aturanTugas = ['id' => 'integer', 'judul' => 'string', 'status' => 'string', 'dibuat_pada' => 'string'];
periksaBentuk('Tugas', $tugas, $aturanTugas, ['status' => $statusValid]);
periksaBentuk('LoginInput', $login, ['email' => 'string', 'sandi' => 'string']);
periksaBentuk('Token', $token, ['token' => 'string']);
periksaBentuk('Error', $error, ['error' => 'array']);
// Satu contoh sengaja salah untuk membuktikan pemeriksa benar-benar bekerja.
$salah = '{"id":5,"judul":"Status ngaco","status":"batal","dibuat_pada":"2026-01-15 08:00:00"}';
periksaBentuk('Tugas-salah', $salah, $aturanTugas, ['status' => $statusValid]);Output yang Dihasilkan:
Tugas : cocok (4 kolom wajib)
LoginInput : cocok (2 kolom wajib)
Token : cocok (1 kolom wajib)
Error : cocok (1 kolom wajib)
Tugas-salah : GAGAL — nilai 'batal' di luar belum/proses/selesaiEmpat contoh sah lolos: json_validate() membuktikan string memang JSON, pemeriksaan kunci memastikan kolom wajib hadir, gettype() mencocokkan tipe, dan pemeriksaan enum menolak nilai di luar daftar. Baris terakhir sengaja mengirim status: batal dan ditolak — pemeriksa yang tidak pernah gagal bukan pemeriksa. Pada proyek nyata, lapisan berikutnya adalah validator OpenAPI eksternal (mis. Spectral atau editor daring Swagger) yang mengurai seluruh berkas YAML, bukan hanya contohnya; pendekatan PHP di atas adalah jaring pertama yang bisa berjalan tanpa alat tambahan.
Menguji Kesesuaian Dokumen dengan Server Nyata #
Pemeriksaan paling meyakinkan tetaplah server yang benar-benar berjalan. Dengan database segar, kita bandingkan response nyata dengan contoh yang tertulis di spesifikasi:
# terminal 1 — server dengan database segar
$ rm -f /tmp/tasks.sqlite
$ TASK_API_DB=/tmp/tasks.sqlite php -S 127.0.0.1:8097 -t public
# terminal 2 — pengguna demo seperti pada Bagian 5, lalu dua pemeriksaan
$ curl -sS http://127.0.0.1:8097/api/kesehatan{"status":"ok"}$ curl -sS -X POST -H 'Content-Type: application/json' \
-d '{"email":"[email protected]","sandi":"sandi-budi-2026"}' \
http://127.0.0.1:8097/api/login{"token":"aad211d18f4edb8113e1c0dc8eb045c22f2f56aa64e97211daf0931dbe803be5"}$ TOKEN=aad211d18f4edb8113e1c0dc8eb045c22f2f56aa64e97211daf0931dbe803be5
$ curl -sS -i -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8097/api/tugas/1HTTP/1.1 200 OK
Host: 127.0.0.1:8097
Date: Tue, 13 Oct 2026 22:33:54 GMT
Connection: close
X-Powered-By: PHP/8.4.23
Content-Type: application/json; charset=utf-8
{"id":1,"judul":"Belajar PHP dasar","status":"selesai","dibuat_pada":"2026-01-05 08:00:00"}Body response terakhir identik dengan contoh pada operasi GET /api/tugas/{id} di openapi.yaml — kolom sama, tipe sama, nilai seed sama. Inilah arti “API sesuai dokumen”: pengguna yang memprogram berdasarkan spesifikasi tidak akan menabrak kejutan. Token pada transkrip tentu berbeda pada setiap login Anda, dan justru itu sesuai deskripsi skema Token yang menyatakan nilainya selalu baru.
Dockerfile: Mengemas API #
Dokumentasi menjangkau pembaca; container menjangkau mesin. Dockerfile berikut mengemas proyek menjadi image yang bisa dijalankan di mana Docker tersedia:
# Image dasar resmi PHP 8.4 CLI.
# Ekstensi pdo_sqlite sudah terpasang bawaan pada image resmi ini,
# jadi tidak ada pekerjaan instalasi ekstensi tambahan.
FROM php:8.4-cli
# Seluruh proyek (composer.json, public/, src/, database/) disalin ke /app.
COPY . /app
WORKDIR /app
# Lokasi berkas database SQLite di dalam container.
# Timpa dari luar tanpa membangun ulang image:
# docker run -e TASK_API_DB=/data/tasks.sqlite ...
ENV TASK_API_DB=/tmp/tasks.sqlite
# Web server bawaan PHP bisa melayani beberapa request secara paralel
# lewat variabel ini (berlaku pada sistem mirip Unix seperti container).
ENV PHP_CLI_SERVER_WORKERS=4
# Port yang didengarkan php -S pada CMD di bawah.
EXPOSE 8080
# Satu proses PHP melayani seluruh request dari front controller
# di dalam direktori public/.
CMD ["php", "-S", "0.0.0.0:8080", "-t", "public"]Mari kita bedah per instruksi. FROM php:8.4-cli memilih image resmi PHP 8.4 varian CLI — cukup untuk proyek kita karena kita tidak memakai Apache ataupun PHP-FPM, dan pdo_sqlite memang sudah terpasang di image resmi tersebut. COPY . /app lalu WORKDIR /app menyalin seluruh proyek dan menjadikannya direktori kerja. ENV TASK_API_DB=/tmp/tasks.sqlite memberi nilai bawaan variabel yang dibaca Database::buat() sejak Bagian 3 — satu-satunya titik konfigurasi yang dibutuhkan, dan bisa ditimpa lewat flag -e tanpa membangun ulang image. EXPOSE 8080 mendokumentasikan port yang dipakai, dan CMD menjalankan perintah yang sudah kita pakai sepanjang proyek — server web bawaan PHP, kali ini mendengarkan 0.0.0.0 (semua antarmuka jaringan container) alih-alih 127.0.0.1.
Catatan Kematangan: Server Bawaan vs PHP-FPM #
Kita perlu jujur tentang apa yang dikemas Dockerfile ini. Server web bawaan PHP (php -S) dirancang untuk pengembangan: secara bawaan ia memproses satu request pada satu waktu, sehingga request yang lambat membuat antrean. Variabel PHP_CLI_SERVER_WORKERS=4 pada Dockerfile mengatasinya sebagian dengan memproses beberapa request paralel, dan untuk API kecil internal itu sering sudah memadai. Namun untuk beban nyata, jalur produksi yang lazim adalah nginx atau Apache di depan PHP-FPM sebagai FastCGI process manager — pemisahan itu memberi manajemen proses worker, konfigurasi TLS, dan buffering yang matang. Detail kedua: berkas SQLite di /tmp akan hilang bersama container; untuk data yang menetap, arahkan TASK_API_DB ke path volume yang di-mount, misalnya docker run -v tasks-data:/data -e TASK_API_DB=/data/tasks.sqlite.
Apa yang Berubah #
Dua berkas baru menutup proyek ini, keduanya di luar kode PHP:
openapi.yaml— spesifikasi OpenAPI 3.1 untuk seluruh API: 8 operasi (2 publik, 6 terlindungi), skemaTugas,TugasInput,TugasPerubahan,LoginInput,Token,Error, skema keamananbearerAuth, serta komponen response 401/404/405/422 yang dipakai ulang lewat$ref.Dockerfile— imagephp:8.4-cliberisi seluruh proyek, variabelTASK_API_DBdanPHP_CLI_SERVER_WORKERS, port 8080, dan CMDphp -Sdari direktoripublic/.
Rekap Tujuh Bagian #
Tujuh bagian membentuk satu aplikasi utuh dari kosong. Tabel berikut memetakan perjalanan itu:
| Bagian | Berkas kunci | Yang diberikan |
|---|---|---|
| 1 — Fondasi API | composer.json, public/index.php | Struktur proyek, autoloading PSR-4, front controller, /api/kesehatan |
| 2 — Routing dan Request | src/Router.php, Request.php, Response.php | Router dengan {param}, 404/405 + Allow, objek request/response immutable |
| 3 — Database dan Repository | src/Database.php, TaskRepository.php, database/schema.sql | PDO SQLite, prepared statements, CRUD tugas, seed deterministik |
| 4 — Validation dan Error | src/Validator.php, ErrorHandler.php | Aturan validasi berantai, bentuk error JSON tunggal, 500 tertangani |
| 5 — Authentication | src/TokenAuth.php, UserRepository.php | Login bcrypt, bearer token ter-hash, route terlindungi, logout |
| 6 — API Testing | tests/bootstrap.php, tests/ApiTest.php | 11 test end-to-end tanpa web server, isolasi :memory: |
| 7 — Dokumentasi dan Deployment | openapi.yaml, Dockerfile | Kontrak tertulis untuk pengguna, kemasan container |
Nilai terbesar proyek ini mungkin bukan aplikasinya — daemon tugas bertebaran di mana-mana — melainkan pola keputusannya: objek request/response yang bisa diuji tanpa HTTP (Bagian 2) dibayar penuh oleh test suite (Bagian 6); repository yang menyembunyikan SQL membuat handler tipis; bentuk error yang konsisten membuat dokumentasi bisa menjanjikan satu skema Error untuk semua endpoint. Pola-pola inilah yang Anda bawa ke proyek berikutnya, apa pun frameworknya.
Cara Menjalankan #
# 1. Pemeriksa bentuk contoh JSON — salin blok "Memeriksa Contoh JSON"
# ke berkas contoh-bentuk.php di dalam folder bagian ini, lalu jalankan
php contoh-bentuk.php
# 2. Uji kesesuaian dokumen vs server nyata (dari root proyek)
rm -f /tmp/tasks.sqlite
TASK_API_DB=/tmp/tasks.sqlite php -S 127.0.0.1:8097 -t public
curl -sS http://127.0.0.1:8097/api/kesehatan
# 3. Bangun dan jalankan container (butuh Docker di mesin Anda)
docker build -t manajemen-tugas-api .
docker run --rm -p 8080:8080 manajemen-tugas-api
curl -sS http://localhost:8080/api/kesehatanPerintah Docker pada poin 3 tidak dijalankan di lingkungan penulisan halaman ini — Docker tidak tersedia di sana — sehingga tidak ada transkripnya; seluruh perilaku yang dikutip di halaman ini berasal dari sesi php -S dan PHPUnit yang nyata. Dua berkas pendukung halaman ini — openapi.yaml dan Dockerfile — tersedia di bagian bawah halaman. Untuk pendalaman, baca spesifikasi OpenAPI 3.1
, image resmi PHP di Docker Hub
, dokumentasi server web bawaan PHP
, serta panduan nginx + PHP-FPM
. Selamat — proyek REST API manajemen tugas selesai, terdokumentasi, dan siap dikemas.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
openapi.yaml
Unduh berkasopenapi: 3.1.0
info:
title: REST API Manajemen Tugas
summary: API JSON untuk mengelola daftar tugas.
description: |
API manajemen tugas yang dibangun dengan PHP murni tanpa framework.
Seluruh response dan request body berbentuk JSON. Endpoint /api/tugas
terlindungi bearer token; minta token lewat POST /api/login, lalu
kirim sebagai header "Authorization: Bearer <token>".
version: 1.0.0
servers:
- url: http://127.0.0.1:8097
description: Server pengembangan lokal (php -S)
- url: http://localhost:8080
description: Container Docker
tags:
- name: kesehatan
description: Pemeriksaan status layanan.
- name: auth
description: Login dan logout bearer token.
- name: tugas
description: CRUD daftar tugas (membutuhkan token).
paths:
/api/kesehatan:
get:
tags: [kesehatan]
summary: Periksa status layanan
responses:
'200':
description: Layanan hidup.
content:
application/json:
schema:
type: object
required: [status]
properties:
status:
type: string
const: ok
example:
status: ok
'405':
$ref: '#/components/responses/MethodNotAllowed'
/api/login:
post:
tags: [auth]
summary: Tukar email dan sandi dengan bearer token
description: |
Satu-satunya cara memperoleh token. Sandi diverifikasi terhadap
hash bcrypt; kegagalan apa pun (email tak dikenal atau sandi salah)
dijawab pesan yang sama.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginInput'
example:
email: [email protected]
sandi: sandi-budi-2026
responses:
'200':
description: Login berhasil; token berlaku 1 jam.
content:
application/json:
schema:
$ref: '#/components/schemas/Token'
example:
token: 79a2e78d8883fe1c4626cb26074d8c6ec1f2c3671cd7b8b322f1368d16594396
'401':
description: Email atau sandi salah.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: Email atau sandi salah.
/api/logout:
post:
tags: [auth]
summary: Cabut token yang dipakai request ini
security:
- bearerAuth: []
responses:
'204':
description: Token dicabut; request berikutnya dengan token yang sama ditolak 401.
'401':
$ref: '#/components/responses/TidakSah'
/api/tugas:
get:
tags: [tugas]
summary: Daftar seluruh tugas
security:
- bearerAuth: []
responses:
'200':
description: Daftar tugas terurut menaik berdasarkan id.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Tugas'
example:
- id: 1
judul: Belajar PHP dasar
status: selesai
dibuat_pada: '2026-01-05 08:00:00'
- id: 2
judul: Merancang REST API
status: proses
dibuat_pada: '2026-01-10 09:30:00'
- id: 3
judul: Menulis dokumentasi API
status: belum
dibuat_pada: '2026-01-12 14:15:00'
'401':
$ref: '#/components/responses/TidakSah'
post:
tags: [tugas]
summary: Buat tugas baru
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TugasInput'
example:
judul: Belajar authentication
status: belum
responses:
'201':
description: Tugas tersimpan.
content:
application/json:
schema:
$ref: '#/components/schemas/Tugas'
example:
id: 4
judul: Belajar authentication
status: belum
dibuat_pada: '2026-10-13 22:20:56'
'401':
$ref: '#/components/responses/TidakSah'
'422':
$ref: '#/components/responses/ValidasiGagal'
/api/tugas/{id}:
get:
tags: [tugas]
summary: Baca satu tugas
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/IdTugas'
responses:
'200':
description: Detail tugas.
content:
application/json:
schema:
$ref: '#/components/schemas/Tugas'
example:
id: 1
judul: Belajar PHP dasar
status: selesai
dibuat_pada: '2026-01-05 08:00:00'
'401':
$ref: '#/components/responses/TidakSah'
'404':
$ref: '#/components/responses/TidakDitemukan'
put:
tags: [tugas]
summary: Ubah tugas sebagian atau seluruhnya
description: |
Kolom yang tidak dikirim membiarkan nilai lama tetap; kolom yang
dikirim divalidasi dengan aturan yang sama seperti pembuatan.
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/IdTugas'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TugasPerubahan'
example:
status: selesai
responses:
'200':
description: Tugas terbarui; seluruh kolom dikirim balik.
content:
application/json:
schema:
$ref: '#/components/schemas/Tugas'
example:
id: 4
judul: Belajar authentication
status: selesai
dibuat_pada: '2026-10-13 22:20:56'
'401':
$ref: '#/components/responses/TidakSah'
'404':
$ref: '#/components/responses/TidakDitemukan'
'422':
$ref: '#/components/responses/ValidasiGagal'
delete:
tags: [tugas]
summary: Hapus satu tugas
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/IdTugas'
responses:
'204':
description: Tugas terhapus; tidak ada body.
'401':
$ref: '#/components/responses/TidakSah'
'404':
$ref: '#/components/responses/TidakDitemukan'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: |
Token 64 karakter heksadesimal dari POST /api/login. Kirim sebagai
header "Authorization: Bearer <token>". Berlaku 1 jam dan bisa
dicabut lewat POST /api/logout.
parameters:
IdTugas:
name: id
in: path
required: true
description: id tugas.
schema:
type: integer
minimum: 1
responses:
TidakSah:
description: Token tidak dikirim, tidak dikenal, atau kedaluwarsa.
headers:
WWW-Authenticate:
description: Selalu berisi nilai Bearer.
schema:
type: string
example: Bearer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHORIZED
message: "Kirim token lewat header Authorization: Bearer <token>."
TidakDitemukan:
description: Tugas dengan id tersebut tidak ada.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: NOT_FOUND
message: Tugas dengan id 99 tidak ditemukan.
ValidasiGagal:
description: Body tidak lolos validasi; details berisi daftar pesan per kolom.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: VALIDATION_ERROR
message: Data yang dikirim tidak valid.
details:
judul:
- "Kolom 'judul' minimal 3 karakter."
status:
- "Kolom 'status' harus salah satu dari: belum, proses, selesai."
MethodNotAllowed:
description: Method tidak diizinkan untuk endpoint ini.
headers:
Allow:
description: Daftar method yang diizinkan, dipisah koma.
schema:
type: string
example: GET
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: METHOD_NOT_ALLOWED
message: Method PUT tidak diizinkan untuk endpoint ini.
schemas:
Tugas:
type: object
required: [id, judul, status, dibuat_pada]
properties:
id:
type: integer
example: 4
judul:
type: string
example: Belajar SQLite
status:
type: string
enum: [belum, proses, selesai]
example: belum
dibuat_pada:
type: string
description: Waktu pembuatan format YYYY-MM-DD HH:MM:SS (waktu server).
example: '2026-01-15 08:00:00'
TugasInput:
type: object
required: [judul]
properties:
judul:
type: string
minLength: 3
maxLength: 100
example: Belajar authentication
status:
type: string
enum: [belum, proses, selesai]
default: belum
TugasPerubahan:
type: object
description: Kolom yang dikirim divalidasi; kolom yang tidak dikirim membiarkan nilai lama.
properties:
judul:
type: string
minLength: 3
maxLength: 100
status:
type: string
enum: [belum, proses, selesai]
LoginInput:
type: object
required: [email, sandi]
properties:
email:
type: string
format: email
example: [email protected]
sandi:
type: string
example: sandi-budi-2026
Token:
type: object
required: [token]
properties:
token:
type: string
description: Token acak 64 karakter heksadesimal; nilai berbeda pada setiap login.
example: 79a2e78d8883fe1c4626cb26074d8c6ec1f2c3671cd7b8b322f1368d16594396
Error:
type: object
required: [error]
properties:
error:
type: object
required: [code, message]
properties:
code:
type: string
description: Kode error yang stabil untuk diprogram klien.
example: NOT_FOUND
message:
type: string
description: Pesan yang ditujukan untuk manusia.
example: Tugas dengan id 99 tidak ditemukan.
details:
type: object
description: Informasi tambahan; hanya muncul bila relevan.