Middleware Hooks
Intersep setiap request dengan hooks.server.ts, susun handler dengan sequence().
Middleware hook memungkinkan Anda menjalankan kode pada setiap request — autentikasi, pencatatan log, injeksi header, dan lainnya.
hooks.server.ts
Buat src/hooks.server.ts dan ekspor fungsi handle:
import type { Handle } from "bosia";
export const handle: Handle = async ({ event, resolve }) => {
// Runs before the route handler
event.locals.requestTime = Date.now();
const response = await resolve(event);
// Runs after the route handler
response.headers.set("X-Custom", "value");
return response;
};Fungsi handle mengintersepsi setiap request halaman dan API. File statis (chunk JS/CSS,
file public/) dilayani sebelum hooks berjalan, sehingga pencarian sesi di handle tidak
berjalan sekali per aset. Request file yang tidak ditemukan tetap melewati hooks sebelum 404.
event.params sudah berisi params dari route yang cocok saat handle berjalan.
Tipe Handle
type Handle = (input: { event: RequestEvent; resolve: ResolveFunction }) => MaybePromise<Response>;event— event request denganrequest,url,params,route,locals,cookiesresolve— panggil ini untuk melanjutkan ke handler berikutnya atau ke route
Menyusun dengan sequence()
Gunakan sequence() untuk menyusun beberapa handler:
import { sequence } from "bosia";
import type { Handle } from "bosia";
const authHandle: Handle = async ({ event, resolve }) => {
event.locals.requestTime = Date.now();
event.locals.user = null; // replace with real session logic
return resolve(event);
};
const langHandle: Handle = async ({ event, resolve }) => {
event.locals.lang = event.request.headers.get("accept-language")?.split(",")[0] ?? "en";
return resolve(event);
};
export const handle = sequence(authHandle, langHandle);Handler dieksekusi dari kiri ke kanan. resolve milik setiap handler memanggil handler berikutnya dalam rantai.
Access log: jangan
console.logsetiap request dari hook. Saat stdout berupa pipe (docker logs, systemd) tiap baris adalah satu syscall, ~11–14µs CPU per request. Serahkan access log ke reverse proxy (nginx, Caddy), atau pasang pluginaccessLog()yang menulis secara batch.
Menyetel Locals
event.locals adalah objek biasa yang dibagikan di antara hooks, loader, dan handler API untuk request saat ini:
// hooks.server.ts
const auth: Handle = async ({ event, resolve }) => {
const session = getSession(event.cookies.get("session_id"));
event.locals.user = session?.user ?? null;
return resolve(event);
};// +page.server.ts — locals are available here
export async function load({ locals }: LoadEvent) {
return { user: locals.user };
}Akses Cookie
Baca dan tulis cookie melalui event.cookies:
const handle: Handle = async ({ event, resolve }) => {
// Read
const token = event.cookies.get("auth_token");
// Write (added to the response)
event.cookies.set("visited", "true", {
path: "/",
httpOnly: true,
maxAge: 60 * 60 * 24, // 1 day
});
// Delete
event.cookies.delete("old_cookie"); // mengikuti path bawaan yang dipakai set()
return resolve(event);
};Pola Umum
Autentikasi
const auth: Handle = async ({ event, resolve }) => {
const token = event.cookies.get("session");
event.locals.user = token ? await validateSession(token) : null;
return resolve(event);
};Pencatatan Log Request
const logger: Handle = async ({ event, resolve }) => {
const start = Date.now();
const res = await resolve(event);
console.log(
`${event.request.method} ${event.url.pathname} → ${res.status} (${Date.now() - start}ms)`,
);
return res;
};Proteksi Route
import { redirect } from "bosia";
const guard: Handle = async ({ event, resolve }) => {
if (event.route.id?.startsWith("/(private)") && !event.locals.user) {
throw redirect(303, "/login");
}
return resolve(event);
};event.route.id adalah folder route yang cocok di bawah src/routes, grup tetap disertakan:
halaman di src/routes/(private)/dashboard/[id]/+page.svelte punya id /(private)/dashboard/[id].
Taruh halaman yang butuh login di bawah (private) dan satu pengecekan ini melindungi semuanya,
termasuk halaman yang ditambahkan nanti. Nilainya null jika tidak ada route yang cocok.
Satu pengecekan menutup semua cara masuk ke halaman: muat halaman penuh, navigasi sisi klien
(router mengambil data loader halaman, dan event.route adalah route halaman tersebut), dan POST
form action. event.isDataRequest membedakan navigasi kalau kamu memang perlu tahu; otorisasi
seharusnya tidak peduli. Lihat Keamanan › Penjaga Rute di Hook.
throw redirect() lebih disarankan daripada mengembalikan Response.redirect(...):
bosia mengubahnya menjadi bentuk yang tepat untuk kedua jenis permintaan, dan
BASE_PATH diterapkan otomatis. Response.redirect
yang di-return tetap bekerja, tapi Location-nya diteruskan apa adanya — di bawah
base path, prefiksnya harus kamu tulis sendiri.
throw error(404, "…") bekerja dengan pola yang sama: halaman error untuk permintaan
dokumen, error boundary router untuk navigasi klien.