Server Loaders
Muat data di server dengan load(), metadata(), dan pengaliran data parent().
Server loader berjalan pada setiap request untuk mengambil data bagi halaman dan layout.
Loader Halaman
Ekspor fungsi load dari +page.server.ts:
import type { LoadEvent } from "bosia";
export async function load({ params, url, locals, cookies }: LoadEvent) {
const post = await db.getPost(params.slug);
return { post };
}Objek yang dikembalikan menjadi prop data di +page.svelte:
<script lang="ts">
let { data } = $props();
</script>
<h1>{data.post.title}</h1><p>{data.post.content}</p>Loader Layout
+layout.server.ts bekerja dengan cara yang sama. Datanya menjadi prop data komponen layout itu, dan dapat dijangkau oleh loader anak melalui parent():
// src/routes/+layout.server.ts
import type { LoadEvent } from "bosia";
export async function load({ locals }: LoadEvent) {
return {
appName: "Bosia Demo",
requestTime: locals.requestTime,
};
}Data layout tidak otomatis digabung ke prop
datahalaman. Tidak seperti SvelteKit, Bosia memisahkan setiap hasil load: propdatapada+page.sveltehanya berisi hasilload()halaman itu sendiri. Membacadata.appNamedi halaman yang loadernya tidak mengembalikanappNamemenghasilkanundefined. Untuk memakai data layout induk di halaman, aliri secara eksplisit denganparent()(di bawah).
Pengaliran Data dengan parent()
Loader anak dapat mengakses data dari loader layout induknya:
// src/routes/blog/[slug]/+page.server.ts
import type { LoadEvent } from "bosia";
export async function load({ params, parent }: LoadEvent) {
const post = await db.getPost(params.slug); // berjalan selagi layout dimuat
const parentData = await parent();
return {
post,
appName: parentData.appName, // from root layout loader
};
}Data mengalir dari atas ke bawah melalui loader: layout root → layout grup → layout halaman → halaman. Setiap loader melihat hasil induknya via parent(), tetapi tidak ada yang otomatis digabung ke prop data komponen — kembalikan ulang key yang Anda perlukan (seperti appName di atas) agar halaman bisa membacanya. Halaman tanpa load() tidak bisa memanggil parent(); tambahkan loader minimal yang mengembalikan await parent() untuk meneruskan data layout.
Loader berjalan paralel
Semua loader layout dan halaman untuk satu request mulai bersamaan. parent() menunggu layout di atasnya selesai, jadi hanya loader yang memanggilnya yang menunggu. Kerjakan hal yang tidak bergantung (seperti db.getPost di atas) sebelum await parent(), agar berjalan bersamaan dengan loader layout.
Jika sebuah layout melempar (redirect(), error(), atau crash), respons berasal dari layout paling atas yang gagal. Tidak ada data halaman yang sampai ke pengunjung. Tetapi kode halaman sebelum await parent() sudah terlanjur berjalan. Jaga rute di hook, atau panggil await parent() lebih dulu di loader yang harus tetap di belakang pengecekan layout.
Saat dev, Bosia memberi peringatan sekali per rute dan guard jika layout melempar redirect() atau error(401/403) sementara loader di bawahnya (layout bersarang, load() halaman, metadata()) sudah berjalan. Datanya dibuang, tetapi penulisan yang sudah terjadi tidak. Peringatan ini soal letak guard, bukan soal apa yang dilakukan kode di bawahnya. Ia juga muncul untuk halaman yang hanya membaca, untuk redirect non-auth seperti prefix locale, dan saat load() memanggil await parent() lebih dulu: itu melindungi loader tersebut, tetapi hanya memindahkan guard ke hook yang menghilangkan peringatannya.
Metadata
Ekspor fungsi metadata untuk mengatur judul halaman dan meta tag:
import type { MetadataEvent, LoadEvent } from "bosia";
export function metadata({ params }: MetadataEvent) {
const post = getPost(params.slug);
return {
title: `${post.title} — My Blog`,
description: `A blog post about ${params.slug}`,
meta: [{ property: "og:title", content: post.title }],
// Pass data to load() — avoids duplicate queries
data: { post },
};
}
export async function load({ params, parent, metadata }: LoadEvent) {
const parentData = await parent();
// Reuse data from metadata() — no duplicate DB query
const post = metadata?.post ?? getPost(params.slug);
return { post, appName: parentData.appName };
}Properti data pada nilai kembalian metadata() diteruskan ke load() sebagai event.metadata. Ini memungkinkan Anda mengambil data sekali dan berbagi di antara kedua fungsi.
Properti LoadEvent
| Properti | Tipe | Deskripsi |
|---|---|---|
url |
URL |
URL request |
params |
Record<string, string> |
Parameter route dinamis |
locals |
Record<string, any> |
Data yang disetel oleh middleware hooks |
cookies |
Cookies |
Membaca/menulis cookies |
fetch |
Function |
Fetch yang sadar sesi (meneruskan cookies) |
parent |
() => Promise<Record> |
Data dari loader layout induk |
metadata |
Record | null |
Data yang diteruskan dari fungsi metadata() |
depends |
(...keys) => void |
Deklarasi kunci invalidasi kustom (lihat Invalidasi Data) |
setHeaders |
(headers) => void |
Setel header response (lihat di bawah) |
Menyetel Header Response
Gunakan setHeaders untuk menambahkan header ke response halaman — misalnya Cache-Control:
// +page.server.ts
export async function load({ setHeaders }: LoadEvent) {
setHeaders({ "cache-control": "public, max-age=60" });
return { posts: await getPosts() };
}Header diterapkan ke response HTML SSR maupun response data navigasi klien. Menyetel header yang sama dua kali (di loader mana pun dalam rantai) akan melempar error, dan set-cookie dilarang — gunakan API cookies. Saat prerendering, setHeaders tidak berefek (hanya file HTML yang ditulis ke disk). Jika loader membaca cookies, response data tetap private, no-cache meskipun loader menyetel cache-control sendiri — privasi mengalahkan niat.
Penanganan Error
Lempar error dari loader untuk menampilkan halaman error:
import { error, redirect } from "bosia";
export async function load({ params }: LoadEvent) {
const post = await db.getPost(params.slug);
if (!post) {
error(404, "Post not found");
}
if (post.isPrivate) {
redirect(303, "/login");
}
return { post };
}Deduplikasi Request
Request identik yang bersamaan berbagi satu loader in-flight secara default. Kunci dedup menyertakan origin request dan hash identitas CACHE_KEYS, jadi route per-user aman di-dedup — host atau session cookie berbeda tidak pernah berbagi hasil loader. Jika aplikasi Anda autentikasi dengan nama cookie atau header kustom, tambahkan ke CACHE_KEYS.
Lihat Deduplikasi Request untuk model lengkap dan aturan keamanannya.
Timeout
Loader memiliki timeout yang dapat dikonfigurasi untuk mencegah response yang tergantung:
| Variabel Env | Default | Deskripsi |
|---|---|---|
LOAD_TIMEOUT |
30000 |
Timeout untuk load() dalam ms. 0 atau Infinity = mati |
METADATA_TIMEOUT |
10000 |
Timeout untuk metadata() dalam ms. 0 atau Infinity = mati |