Autentikasi
Registrasi dan login. Password di-hash dengan bcrypt, dan login mengembalikan JWT yang kedaluwarsa setelah 24 jam.
PROYEK 02 · REST API BACKEND
Task API adalah REST API yang ditulis dengan Go, Gin, dan GORM. Pengguna mendaftar, login dengan JWT, dan mengelola task miliknya sendiri. API ini berjalan di SQLite tanpa setup database dan dilengkapi dokumentasi Swagger serta koleksi Postman.
01 POST /api/auth/register
02 POST /api/auth/login
03 GET /api/me
04 GET /api/tasks
05 POST /api/tasks
{
"data": [],
"meta": { "page": 1, "limit": 10, "total": 0 }
}01 / RUANG LINGKUP
Setiap pengguna punya daftar task pribadi. Fokus proyek ini ada pada bagian di luar CRUD biasa: autentikasi, akses yang dibatasi untuk pemilik, validasi input, pagination, dan dokumentasi yang bisa dijalankan developer lain.
02 / ISI PROYEK
Registrasi dan login. Password di-hash dengan bcrypt, dan login mengembalikan JWT yang kedaluwarsa setelah 24 jam.
Membuat, membaca, mengubah, dan menghapus task sendiri, dengan daftar per halaman dan filter selesai.
Route di main.go, dengan handler, middleware JWT, model, dan utilitas token di package terpisah.
Spesifikasi OpenAPI dengan Swagger UI di /docs, koleksi Postman, dan Dockerfile multi-stage.
ENDPOINT API
| METHOD | PATH | AKSES | CATATAN |
|---|---|---|---|
| GET | /health | Publik | Memeriksa status server. |
| POST | /api/auth/register | Publik | Membuat akun. Password di-hash dengan bcrypt; email yang sudah terdaftar menghasilkan 409. |
| POST | /api/auth/login | Publik | Mengembalikan JWT yang berlaku 24 jam. |
| GET | /api/me | Perlu login | Profil pengguna yang sedang login. |
| GET | /api/tasks | Perlu login | Task milikmu, terbaru lebih dulu. Query: page, limit, done. |
| POST | /api/tasks | Perlu login | Membuat task. Judul wajib diisi. |
| GET | /api/tasks/:id | Perlu login | Satu task milikmu. Task milik pengguna lain menghasilkan 404. |
| PUT | /api/tasks/:id | Perlu login | Update sebagian: kirim hanya field yang ingin diubah. |
| DELETE | /api/tasks/:id | Perlu login | Menghapus salah satu task milikmu. |
REQUEST TEREKAM
15 request direkam dari API yang berjalan di laptop saya pada 20 Oktober 2026. Token dipersingkat.
/health200Memeriksa server berjalanGET /healthHTTP 200 OK { "status": "ok" }
/api/auth/register201Membuat akunPOST /api/auth/register Content-Type: application/json { "name": "Rafi", "email": "rafi.demo@example.com", "password": "rahasia123" }
HTTP 201 Created { "data": { "id": 1, "name": "Rafi", "email": "rafi.demo@example.com", "created_at": "2026-10-20T15:38:56.913128552+07:00" }, "message": "registrasi berhasil" }
/api/auth/register409Email yang sama ditolakPOST /api/auth/register Content-Type: application/json { "name": "Rafi", "email": "rafi.demo@example.com", "password": "rahasia123" }
HTTP 409 Conflict { "error": "email sudah terdaftar" }
/api/auth/register400Input tidak valid ditolakPOST /api/auth/register Content-Type: application/json { "name": "Rafi", "email": "not-an-email", "password": "123" }
HTTP 400 Bad Request { "error": "Key: 'registerInput.Email' Error:Field validation for 'Email' failed on the 'email' tag\nKey: 'registerInput.Password' Error:Field validation for 'Password' failed on the 'min' tag" }
/api/auth/login200Login dan menerima JWTPOST /api/auth/login Content-Type: application/json { "email": "rafi.demo@example.com", "password": "rahasia123" }
HTTP 200 OK { "expires_in": 86400, "token": "eyJhbGciOiJIUzI1Ni…", "token_type": "Bearer" }
/api/auth/login401Password salah menghasilkan 401POST /api/auth/login Content-Type: application/json { "email": "rafi.demo@example.com", "password": "salah-total" }
HTTP 401 Unauthorized { "error": "email atau password salah" }
/api/me200Profil pengguna yang loginGET /api/me Authorization: Bearer eyJhbGciOiJIUzI1Ni…
HTTP 200 OK { "data": { "id": 1, "name": "Rafi", "email": "rafi.demo@example.com", "created_at": "2026-10-20T15:38:56.913128552+07:00" } }
/api/tasks401Tanpa token, tidak ada aksesGET /api/tasksHTTP 401 Unauthorized { "error": "token tidak ditemukan" }
/api/tasks201Membuat taskPOST /api/tasks Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1Ni… { "title": "Belajar goroutine", "description": "Baca dokumentasi dan buat contoh" }
HTTP 201 Created { "data": { "id": 1, "user_id": 1, "title": "Belajar goroutine", "description": "Baca dokumentasi dan buat contoh", "done": false, "created_at": "2026-10-20T15:38:57.067602292+07:00", "updated_at": "2026-10-20T15:38:57.067602292+07:00" } }
/api/tasks201Membuat task keduaPOST /api/tasks Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1Ni… { "title": "Menulis tes untuk handler" }
HTTP 201 Created { "data": { "id": 2, "user_id": 1, "title": "Menulis tes untuk handler", "description": "", "done": false, "created_at": "2026-10-20T15:38:57.112172832+07:00", "updated_at": "2026-10-20T15:38:57.112172832+07:00" } }
/api/tasks?page=1&limit=10200Daftar task dengan paginationGET /api/tasks?page=1&limit=10 Authorization: Bearer eyJhbGciOiJIUzI1Ni…
HTTP 200 OK { "data": [ { "id": 2, "user_id": 1, "title": "Menulis tes untuk handler", "description": "", "done": false, "created_at": "2026-10-20T15:38:57.112172832+07:00", "updated_at": "2026-10-20T15:38:57.112172832+07:00" }, { "id": 1, "user_id": 1, "title": "Belajar goroutine", "description": "Baca dokumentasi dan buat contoh", "done": false, "created_at": "2026-10-20T15:38:57.067602292+07:00", "updated_at": "2026-10-20T15:38:57.067602292+07:00" } ], "meta": { "limit": 10, "page": 1, "total": 2 } }
/api/tasks/1200Mengubah satu field sajaPUT /api/tasks/1 Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1Ni… { "done": true }
HTTP 200 OK { "data": { "id": 1, "user_id": 1, "title": "Belajar goroutine", "description": "Baca dokumentasi dan buat contoh", "done": true, "created_at": "2026-10-20T15:38:57.067602292+07:00", "updated_at": "2026-10-20T15:38:57.133681456+07:00" } }
/api/tasks?done=true200Filter berdasarkan doneGET /api/tasks?done=true Authorization: Bearer eyJhbGciOiJIUzI1Ni…
HTTP 200 OK { "data": [ { "id": 1, "user_id": 1, "title": "Belajar goroutine", "description": "Baca dokumentasi dan buat contoh", "done": true, "created_at": "2026-10-20T15:38:57.067602292+07:00", "updated_at": "2026-10-20T15:38:57.133681456+07:00" } ], "meta": { "limit": 10, "page": 1, "total": 1 } }
/api/tasks/9999404Task yang tidak adaGET /api/tasks/9999 Authorization: Bearer eyJhbGciOiJIUzI1Ni…
HTTP 404 Not Found { "error": "task tidak ditemukan" }
/api/tasks/2200Menghapus taskDELETE /api/tasks/2 Authorization: Bearer eyJhbGciOiJIUzI1Ni…
HTTP 200 OK { "message": "task dihapus" }
KEPUTUSAN DESAIN
Task diambil dengan id task dan id pengguna dari token sekaligus, sehingga pengguna tidak bisa membaca, mengubah, atau menghapus task milik orang lain. Task seperti itu menghasilkan 404, sama seperti task yang memang tidak ada.
Endpoint update membaca title, description, dan done sebagai pointer. Dengan begitu field yang tidak dikirim dibedakan dari nilai kosong atau false: done: false diterapkan, sedangkan field yang tidak dikirim dibiarkan.
Password di-hash dengan bcrypt dan tidak pernah muncul di respons. Login memberi pesan yang sama untuk email salah maupun password salah. Token berupa JWT HS256 yang kedaluwarsa setelah 24 jam, dan parser hanya menerima metode tanda tangan itu.
Endpoint daftar memakai halaman 1 dengan 10 item sebagai bawaan, menerima limit 1 sampai 100 dan kembali ke 10 untuk nilai lain, serta mengirim objek meta berisi page, limit, dan total agar klien bisa membuat pager sendiri.
GORM memakai driver SQLite murni Go, sehingga API langsung jalan dengan go run tanpa perlu memasang apa pun. Database-nya satu file yang diatur lewat DB_PATH.
03 / LANGKAH BERIKUTNYA
API ini belum punya tes otomatis, dan secret JWT memakai nilai bawaan bila variabel lingkungan tidak diisi, jadi di production aplikasi seharusnya menolak berjalan tanpa secret. Langkah berikutnya: tes untuk handler, pembatasan laju di login, dan opsi PostgreSQL.