92 Studi Kasus: Aplikasi Todo dengan Auth dan DB
Dalam dunia pengembangan perangkat lunak, membangun aplikasi to-do sederhana mungkin terdengar basic. Namun, ketika kita memasukkan elemen otentikasi (auth) dan integrasi basis data (DB), kompleksitasnya menjadi nyata—dan dalam realitas produksi, hal-hal “kecil” ini sering menjadi pijakan keterampilan inti seorang software engineer.
Di artikel kali ini, saya ingin membagikan sebuah studi kasus “ke-92” dari pengalaman saya membimbing tim pada bootcamp. Kita akan membahas bagaimana membangun aplikasi todo dengan auth dan DB—dengan contoh praktik, diagram, serta simulasi edge case yang sering diabaikan. Untuk studi kasus ini, saya akan pakai stack Express.js, MongoDB, dan JWT.
1. Overview Arsitektur
Mari kita lihat flow aplikasi ini secara garis besar.
Diagram Alur Mermaid
flowchart LR A[User] -->|Register/Login| B[API Auth Route] B -->|JWT Token| A A -->|Token| C[API Todo Route] C -->|CRUD Operation| D[Database (Todos Collection)]
Garis besarnya:
- User mendaftar/login, server mengirim JWT token.
- Token tersebut digunakan untuk mengakses route TODO.
- Route TODO melakukan operasi CRUD ke database.
- Setiap todo dikaitkan ke user-nya (auth).
2. Struktur Database
Aplikasi membutuhkan dua koleksi (MongoDB):
Collection | Fields |
---|---|
users | _id, username, password_hash |
todos | _id, user_id, title, description, is_done |
Misal, dokumen todo seperti:
{
"_id": "664f3bf...",
"user_id": "664f3be...",
"title": "Beli Kopi",
"description": "Pastikan arabika!",
"is_done": false
}
3. Kode Inti: Express + MongoDB + JWT
Mari kita breakdown kode dalam tiga bagian utama.
a. Setup Auth
// authController.js
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('./models/User');
// REGISTER
exports.register = async (req, res) => {
const {username, password} = req.body;
const hash = await bcrypt.hash(password, 8);
const user = await User.create({username, password_hash: hash});
res.status(201).json({message: 'User registered', user: user.username});
};
// LOGIN
exports.login = async (req, res) => {
const {username, password} = req.body;
const user = await User.findOne({username});
if (!user) return res.status(401).json({message: 'User not found'});
const isMatch = await bcrypt.compare(password, user.password_hash);
if (!isMatch) return res.status(401).json({message: 'Invalid password'});
const token = jwt.sign({userId: user._id}, process.env.JWT_SECRET, {expiresIn: '1h'});
res.json({token});
};
b. Middleware Auth
// middleware/auth.js
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.status(401).json({message: 'Token missing'});
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(401).json({message: 'Invalid/expired token'});
}
};
c. CRUD Todo
// todoController.js
const Todo = require('./models/Todo');
// GET ALL TODOs (per user)
exports.getTodos = async (req, res) => {
const todos = await Todo.find({user_id: req.user.userId});
res.json(todos);
};
// CREATE
exports.createTodo = async (req, res) => {
const todo = await Todo.create({
user_id: req.user.userId,
title: req.body.title,
description: req.body.description,
is_done: false,
});
res.status(201).json(todo);
};
// UPDATE
exports.updateTodo = async (req, res) => {
const todo = await Todo.findOneAndUpdate(
{_id: req.params.id, user_id: req.user.userId},
req.body,
{new: true}
);
if (!todo) return res.status(404).json({message: 'Todo not found'});
res.json(todo);
};
// DELETE
exports.deleteTodo = async (req, res) => {
const result = await Todo.deleteOne({_id: req.params.id, user_id: req.user.userId});
if (result.deletedCount === 0) return res.status(404).json({message: 'Todo not found'});
res.json({message: 'Deleted'});
};
4. Simulasi: Alur Request & Edge Case
Mari kita simulasi edge case yang sering terjadi:
a. User mencoba mengakses data user lain
Klien mengirimkan request:
DELETE /api/todo/62e2d3...
Authorization: Bearer xyz...
Endpoint mencari todo berdasarkan _id
DAN user_id
:
Todo.deleteOne({_id: req.params.id, user_id: req.user.userId});
Jika todo itu milik user lain? Tidak ditemukan. Response:
{
"message": "Todo not found"
}
Best practice: Hindari return 403, cukup 404 untuk menghindari information leakage.
b. Token Kadaluarsa
Jika JWT expired, semua akses auth-protected langsung terpental.
{
"message": "Invalid/expired token"
}
5. Testing Skenario di Postman
Skenario | Endpoint | Hasil |
---|---|---|
Register User | POST /api/auth/register | 201, ditambah username |
Login User | POST /api/auth/login | JWT token dalam JSON |
Get Todos (tanpa auth) | GET /api/todo | 401 Unauthorized |
Get Todos (dengan auth) | GET /api/todo | Daftar todo milik user terkait token |
Update Todo | PUT /api/todo/:id | 200 OK jika punya, 404 kalau bukan milik user |
Delete Todo milik user lain | DELETE /api/todo/:id | 404 Not Found |
6. Opsional: Integrasi Frontend
Frontend bisa menggunakan React. Store token di localStorage atau cookie (HttpOnly jika concern XSS). Ajarkan user untuk handling expired token (logout otomatis/refresh).
7. Praktik Produksi
Beberapa tips ketika deploy ke production:
- Enkripsi JWT Secret baik-baik (jangan hardcoded).
- Jangan pernah log password user.
- Limit request rate memakai express-rate-limit untuk mencegah brute-force login.
- Sanitasi input untuk mencegah injection (meski MongoDB butuh attention khusus).
- Monitoring error via log tailing seperti Sentry.
8. Studi Kasus Performa (Bonus Tabel)
Jumlah User | Rata2 Waktu Register | Waktu Akses Todo | Size Collection |
---|---|---|---|
1000 | 120ms | 25ms | 140KB |
10000 | 130ms | 28ms | 1.2MB |
100000 | 135ms | 33ms | 12MB |
(Test pada Mongo Atlas gratisan)
9. Kesimpulan & Improvement
Membangun aplikasi to-do dengan otentikasi dan database tampak sederhana di atas kertas, tapi begitu banyak aspek production-grade yang perlu diperhatikan: relay JWT, design skema DB, kontrol akses, hingga handling edge case. Studi kasus ke-92 ini membuktikan: “simplicity is deceptive—implementation is where the devil hides.”
Possible improvement:
- Refresh token flow.
- Soft delete (arsip, bukan hapus).
- Email verification/reset password.
- Logging yang comply GDPR.
Jika Anda ingin diskusi lebih lanjut seputar pattern otentikasi skala startup atau best practice deployment ke cloud, silakan komen—pengalaman di lapangan adalah guru terbaik, dan kita belajar studi kasus riil, bukan sekedar teori.
**Happy debugging! 🚀**
91 Studi Kasus: Sistem Manajemen Artikel (CMS)
Artikel Terhangat
99 Kontribusi ke Open Source graphql-go
10 Oct 2025
98 Membuat Plugin Sendiri untuk graphql-go
10 Oct 2025
96 Migrasi REST API ke GraphQL
10 Oct 2025
94 Studi Kasus: Dashboard Admin Real-time
10 Oct 2025

99 Kontribusi ke Open Source graphql-go

98 Membuat Plugin Sendiri untuk graphql-go

96 Migrasi REST API ke GraphQL
