Membangun REST API dengan yii\rest\ActiveController: routing RESTful otomatis, serializers dan format JSON, autentikasi lewat token bearer, rate limiting, serta behaviors seperti timestamp, soft delete, dan blameable yang menempel ke siklus hidup model.

Aplikasi modern jarang berjalan sendirian: aplikasi mobile, SPA, atau sistem pihak ketiga membutuhkan akses data lewat REST API. Di episode 9 kalian membangun autentikasi untuk pengguna web — di episode 10 ini kita membangun API yang aman untuk mesin.
Yii menangani REST API dengan pendekatan yang sama seperti bagian lain framework: sedikit konfigurasi, banyak hasil. Dengan yii\rest\ActiveController saja, Yii menghasilkan endpoint CRUD lengkap dalam format JSON. Ditambah behaviors — kode yang "menempel" pada model untuk mengotomatiskan timestamp, pencatatan pembuat, dan soft delete — API kalian menjadi production-ready dengan sangat sedikit kode manual.
Pertama, daftarkan module api dengan UrlRule yang memetakan controller ke endpoint REST:
'modules' => [
'api' => [
'class' => 'app\modules\api\Module',
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enableStrictParsing' => true,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => 'post'],
],
],
],
],
],yii\rest\UrlRule menghasilkan mapping RESTful secara otomatis:
| Method + URL | Action | Fungsi |
|---|---|---|
GET /api/posts | actionIndex | Daftar post (dengan pagination) |
GET /api/posts/5 | actionView | Detail post 5 |
POST /api/posts | actionCreate | Buat post baru |
PUT/PATCH /api/posts/5 | actionUpdate | Ubah post 5 |
DELETE /api/posts/5 | actionDelete | Hapus post 5 |
Controller API mewarisi yii\rest\ActiveController — ia sudah menyediakan kelima action di atas:
<?php
namespace app\modules\api\controllers;
use yii\rest\ActiveController;
class PostController extends ActiveController
{
public $modelClass = 'app\models\Post';
public function behaviors(): array
{
$behaviors = parent::behaviors();
// hanya JSON, bukan HTML form
$behaviors['contentNegotiator'] = [
'class' => \yii\filters\ContentNegotiator::class,
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
];
return $behaviors;
}
}Satu konfigurasi modelClass saja sudah cukup untuk CRUD penuh. Yii menangani:
GET /api/posts?page=2&per-page=20 — header X-Pagination-* berisi total dan halaman.toArray().201 untuk create, 204 untuk delete, 422 untuk validasi gagal, 404 untuk data tak ada.{ "name": "InvalidArgumentException", "message": "...", "errors": {...} }.Untuk mengendalikan kolom apa yang keluar, timpa fields() di model:
public function fields(): array
{
return [
'id',
'title',
'slug',
'status',
'read_count',
'created_at',
];
}Ini juga soal keamanan: jangan pernah mengekspos password_hash, auth_key, atau access_token lewat serialisasi default. Pastikan field sensitif di-exclude dengan fields() atau extraFields().
API tidak boleh bergantung pada cookie session — ia dipakai oleh mesin dan mobile app. Yii menyediakan autentikasi berbasis token lewat yii\web\User + HttpBearerAuth:
public function behaviors(): array
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => \yii\filters\auth\HttpBearerAuth::class,
];
return $behaviors;
}HttpBearerAuth membaca token dari header Authorization: Bearer <token>, lalu memanggil findIdentityByAccessToken() yang sudah kalian implementasikan di episode 9. Untuk development yang lebih sederhana, QueryParamAuth membaca token dari parameter URL ?access-token=....
Tambahkan kolom access_token di tabel users (migration), lalu:
public function generateAccessToken(): void
{
$this->access_token = Yii::$app->security->generateRandomString(64);
}Saat user login, buat token baru dan simpan. Token inilah yang dibawa client di header setiap request.
Endpoint publik wajib dibatasi agar tidak di-spam. Yii menyediakan RateLimiter — cukup implementasikan yii\filters\RateLimitInterface di model user:
public function getRateLimit($request, $action): array
{
return [100, 3600]; // 100 request per jam
}
public function loadAllowance($request, $action): array
{
return [$this->rate_allowance, $this->rate_allowance_updated_at];
}
public function saveAllowance($request, $action, $allowance, $timestamp): void
{
$this->rate_allowance = $allowance;
$this->rate_allowance_updated_at = $timestamp;
$this->save(false);
}$behaviors['rateLimiter'] = [
'class' => \yii\filters\RateLimiter::class,
];Client yang melebihi kuota menerima 429 Too Many Requests dengan header X-Rate-Limit-Remaining — perlindungan standar yang sering dilupakan dan sangat mudah diaktifkan di Yii.
Warning
Ketiga filter — ContentNegotiator, authenticator, rateLimiter — ditambahkan ke behaviors() dengan cara merge dari parent::behaviors(). Jika kalian menimpa behaviors() tanpa memanggil parent::behaviors(), filter bawaan (seperti CORS dan verb filter) akan hilang dan API kalian bisa menjadi tidak aman tanpa disadari.
Behaviors adalah cara Yii menempelkan kode ke model tanpa mengubah class model itu sendiri. Inilah tiga yang paling sering dipakai di aplikasi enterprise.
Mengisi created_at/updated_at secara otomatis:
use yii\behaviors\TimestampBehavior;
public function behaviors(): array
{
return [
TimestampBehavior::class,
];
}Default mengisi kolom created_at dan updated_at dengan waktu saat ini setiap save(). Perhatikan: ini terjadi di event model (EVENT_BEFORE_INSERT/EVENT_BEFORE_UPDATE) — itulah mengapa kita belajar event system di episode 2.
Mencatat siapa yang membuat/mengubah record — created_by/updated_by diisi Yii::$app->user->id:
use yii\behaviors\BlameableBehavior;
public function behaviors(): array
{
return [
[
'class' => BlameableBehavior::class,
'createdByAttribute' => 'created_by',
'updatedByAttribute' => 'updated_by',
],
];
}Ini memberi kalian audit trail tanpa satu baris pun di controller — siapa pun yang menyimpan data lewat API maupun web tercatat otomatis.
Menghapus data secara permanen berisiko: data bisnis yang "dihapus" bisa dibutuhkan untuk audit. Soft delete menandai record dengan kolom deleted_at alih-alih menghapusnya. Yii2 tidak menyediakan bawaan, tapi mudah dibangun dengan behavior custom:
<?php
namespace app\behaviors;
use yii\base\Behavior;
use yii\db\ActiveRecord;
class SoftDeleteBehavior extends Behavior
{
public string $deletedAtAttribute = 'deleted_at';
public function events(): array
{
return [
ActiveRecord::EVENT_BEFORE_DELETE => 'softDelete',
];
}
public function softDelete($event): void
{
$this->owner->{$this->deletedAtAttribute} = date('Y-m-d H:i:s');
$this->owner->save(false);
$event->isValid = false; // cegah hard delete
}
}public function behaviors(): array
{
return [
[
'class' => \app\behaviors\SoftDeleteBehavior::class,
],
];
}Sekarang $post->delete() hanya menandai deleted_at, dan query default bisa mengecualikan record yang terhapus.
Gabungkan semuanya dalam satu API user yang aman:
<?php
namespace app\modules\api\controllers;
use app\models\User;
use yii\filters\auth\HttpBearerAuth;
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = User::class;
public function behaviors(): array
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::class,
];
$behaviors['rateLimiter'] = [
'class' => \yii\filters\RateLimiter::class,
];
return $behaviors;
}
}Uji dengan curl:
curl -s http://localhost:8080/api/posts \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-X POST -d '{"title":"API pertama","slug":"api-pertama","body":"Halo"}'Respons 201 dengan JSON model berarti API kalian hidup dan terautentikasi.
Inti yang harus dibawa pulang:
yii\rest\UrlRule memetakan controller ke endpoint RESTful CRUD secara otomatis.ActiveController menyediakan action penuh; sesuaikan output dengan fields().HttpBearerAuth/QueryParamAuth + token dari access_token.RateLimitInterface.TimestampBehavior, BlameableBehavior, dan soft delete menempel ke event model.Di episode 11 selanjutnya, kita mengoptimalkan performa: caching & performance — cache component berbasis File, Redis, Memcached, dan APCu, data caching untuk hasil query, fragment caching untuk potongan view, serta HTTP caching dengan Last-Modified dan ETag. Inilah pembeda aplikasi yang cepat di bawah beban. Sampai jumpa di episode 11!