Study
Rocket

Kombinasi Guard, State, dan Json

Penjelasan

Handler nyata di Rocket biasanya menggabungkan beberapa hal sekaligus: request guard untuk autentikasi, `&State<T>` untuk akses data bersama, path parameter untuk id resource, dan `Json<T>` untuk data yang dikirim client. Topik ini merangkum semuanya, dipraktikkan bersamaan dalam satu handler yang realistis. Berbeda dari Axum yang punya aturan URUTAN ketat untuk extractor (body harus di posisi akhir), Rocket TIDAK punya aturan urutan parameter seperti itu — kamu bisa menaruh `Json<T>` di posisi mana pun dalam signature fungsi, karena Rocket mencocokkan parameter berdasarkan TIPE dan NAMA (untuk path/query), bukan posisi. Ini salah satu keuntungan ergonomis dari pendekatan macro-based Rocket dibanding extractor berbasis urutan di Axum. Contoh kasus nyata: endpoint update catatan (`PUT /notes/<id>`) butuh guard autentikasi, id dari path, state untuk akses data, dan body JSON berisi data baru — semuanya dideklarasikan sebagai parameter fungsi tanpa aturan urutan khusus.

Contoh Konsep

#[macro_use] extern crate rocket;

use rocket::State;
use rocket::serde::{Deserialize, Serialize, json::Json};
use rocket::request::{FromRequest, Request, Outcome};
use rocket::http::Status;

struct AppState;

struct AuthUser(String);

#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthUser {
    type Error = ();
    async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
        match req.headers().get_one("x-user") {
            Some(u) => Outcome::Success(AuthUser(u.to_string())),
            None => Outcome::Error((Status::Unauthorized, ())),
        }
    }
}

#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
struct UpdateNote { title: String }

#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
struct NoteResponse { id: u32, title: String, updated_by: String }

// Tidak ada aturan urutan parameter yang wajib di Rocket
#[put("/notes/<id>", data = "<payload>")]
fn update_note(
    id: u32,
    user: AuthUser,
    _state: &State<AppState>,
    payload: Json<UpdateNote>,
) -> Json<NoteResponse> {
    Json(NoteResponse { id, title: payload.title.clone(), updated_by: user.0 })
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .manage(AppState)
        .mount("/", routes![update_note])
}

Praktikum

Susun handler `add_comment` untuk POST "/notes/<note_id>/comments" yang menerima `note_id: u32`, guard `AuthUser`, dan `Json<NewComment>` (urutan bebas karena Rocket tidak mensyaratkan urutan tertentu).

Editor Latihan

Ketik/edit bebas di sini untuk latihan — kode ini tidak dijalankan.

Tips

Kalau kamu baru pindah dari Axum ke Rocket (atau sebaliknya), aturan urutan extractor Axum vs bebas-urutan Rocket ini adalah salah satu perbedaan paling praktis yang akan sering bikin bingung di awal — di Axum urutan menentukan validitas kompilasi (karena berbasis konsumsi body secara berurutan), sedangkan di Rocket setiap parameter di-resolve independen lewat trait `FromRequest`/`FromParam`/`FromData` masing-masing, jadi urutan penulisan hanya soal gaya membaca kode, bukan soal kebenaran program.