Proyek: REST API Todo Lengkap
Bangun API todo dari nol: auth Sanctum, CRUD, dan test.
Target Akhir
Di modul ini kamu membangun REST API Todo yang production-grade: register/login mengeluarkan Sanctum token, CRUD todo (title, done) yang hanya bisa diakses pemiliknya, paginasi 10 per halaman, validasi via Form Request, response konsisten via API Resource, dan policy pengunci akses. Selesaikan ini dengan test hijau, dan fondasi API Laravel-mu solid.
Langkah 1: Setup API dan Database
composer create-project laravel/laravel todo-api
cd todo-api
php artisan install:api # memasang Sanctum + routes/api.php
php artisan make:model Todo -m// database/migrations/xxxx_xx_xx_create_todos_table.php
Schema::create('todos', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title', 255);
$table->boolean('done')->default(false);
$table->timestamps();
});// app/Models/Todo.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Todo extends Model
{
protected $fillable = ['title', 'done'];
protected $casts = ['done' => 'boolean'];
public function user()
{
return $this->belongsTo(User::class);
}
}Jalankan php artisan migrate.
Langkah 2: Auth Controller
// app/Http/Controllers/Api/AuthController.php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller
{
public function register(Request $request)
{
$data = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
]);
$user = User::create([...$data, 'password' => Hash::make($data['password'])]);
return response()->json([
'token' => $user->createToken('api')->plainTextToken,
], 201);
}
public function login(Request $request)
{
$data = $request->validate(['email' => 'required|email', 'password' => 'required']);
$user = User::where('email', $data['email'])->first();
if (! $user || ! Hash::check($data['password'], $user->password)) {
return response()->json(['message' => 'Kredensial salah.'], 401);
}
return response()->json(['token' => $user->createToken('api')->plainTextToken]);
}
}Langkah 3: Form Request dan Resource
// app/Http/Requests/StoreTodoRequest.php
public function rules(): array
{
return ['title' => 'required|string|max:255', 'done' => 'sometimes|boolean'];
}// app/Http/Resources/TodoResource.php
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'done' => $this->done,
'created_at' => $this->created_at->toISOString(),
];
}Langkah 4: Policy dan Controller
php artisan make:policy TodoPolicy --model=Todo// app/Policies/TodoPolicy.php
public function view(User $user, Todo $todo): bool
{
return $user->id === $todo->user_id;
}
// update() dan delete() polanya identik// app/Http/Controllers/Api/TodoController.php
class TodoController extends Controller
{
public function index()
{
// query via relasi: user mustahil melihat todo orang lain
return TodoResource::collection(
auth()->user()->todos()->latest()->paginate(10)
);
}
public function store(StoreTodoRequest $request)
{
$todo = auth()->user()->todos()->create($request->validated());
return new TodoResource($todo);
}
public function show(Todo $todo)
{
$this->authorize('view', $todo);
return new TodoResource($todo);
}
public function update(StoreTodoRequest $request, Todo $todo)
{
$this->authorize('update', $todo);
$todo->update($request->validated());
return new TodoResource($todo);
}
public function destroy(Todo $todo)
{
$this->authorize('delete', $todo);
$todo->delete();
return response()->json(null, 204);
}
}Ini defense in depth: policy menjaga show/update/destroy, scope relasi menjaga index.
Langkah 5: Routes dan Test Manual
// routes/api.php
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('todos', TodoController::class);
});# register -> simpan tokennya
curl -X POST http://localhost:8000/api/register -H "Content-Type: application/json" \
-d '{"name":"Budi","email":"[email protected]","password":"rahasia123","password_confirmation":"rahasia123"}'
# buat todo (ganti TOKEN)
curl -X POST http://localhost:8000/api/todos -H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" -d '{"title":"Belajar Laravel"}'
# list dengan paginasi
curl http://localhost:8000/api/todos -H "Authorization: Bearer TOKEN"Checklist Verifikasi
- Register 201 + token, login salah 401
- Tanpa token, semua route todo 401
- User A tidak bisa akses todo user B (403)
- Title kosong 422 dengan pesan error
- Response index punya
data,links,meta
Catatan teknis:
Route::apiResourceotomatis menghilangkan routecreate/edityang butuh form HTML. Untuk production, tambahkanthrottlepada route login untuk menghambat brute force, dan hapus token lama saat login baru agar token tidak menumpuk.
Tantangan
Rancang Dulu
Sebelum coding: tulis daftar endpoint (method + URI + auth + response) untuk API todo di kertas/catatan. Minimal 7 endpoint. Ini melatih API design sebelum implementasi.
Tugas
Tugas: API Todo
Bangun REST API Todo LENGKAP dari nol sesuai spesifikasi modul ini. Kirimkan file-file kuncimu (migration, model, controller, resource, policy, routes/api.php) atau ringkasan endpoint + bukti test Postman. AI tutor akan menilai kelengkapan dan keamanan.
Kriteria penilaian:
- Semua endpoint CRUD + auth berfungsi
- Policy: user tidak bisa akses todo milik orang lain
- Validasi via Form Request, status code tepat
- Response konsisten via ApiResource + paginasi
AI tutor akan memeriksa bug, kesalahan syntax, dan memberi saran perbaikan.