Load関数の基礎

このページでは、SvelteKit の中核機能である Load 関数の基本的な使い方を学びます。

Load 関数とは何か?

Load 関数は、SvelteKit におけるデータフェッチングの中心的な仕組みです。ページやレイアウトがレンダリングされる前に必要なデータを取得し、コンポーネントに渡す役割を担います。

主な特徴

  1. 自動実行: SvelteKit がページ遷移時に自動的に load 関数を呼び出し、結果をコンポーネントに渡す(開発者が手動で呼び出す必要がない)
  2. 非同期処理: async/awaitを使った非同期データ取得が可能
  3. 型安全: TypeScript との完全な統合により型安全性を保証
  4. 最適化: SvelteKit が自動的にキャッシュやプリフェッチを最適化
「自動実行」の意味

自動実行とは「開発者が明示的に呼び出さなくても良い」という意味です。

React 等では、

// React - useEffectで手動実行
useEffect(() => {
	fetchData(); // 開発者が明示的に呼び出す
}, []);
javascript

SvelteKit では、

// +page.ts に定義するだけ
export const load: PageLoad = async ({ params, fetch }) => {
	// paramsやfetchの型が自動推論される
	return {
		post: await fetch(`/api/posts/${params.id}`).then((r) => r.json()),
	};
};
// コンポーネント側でload()を呼ぶ必要はない!
typescript

なぜ Load 関数が必要か?

従来の SPA では、コンポーネントのマウント後にデータを取得することが一般的でした。しかし、これには以下の問題があります。

  • SEO の問題: 初期 HTML にデータが含まれない
  • パフォーマンス: 画面表示後にローディングが発生
  • ウォーターフォール: 親子コンポーネントで順次データ取得

Load 関数はこれらの問題を解決します。

データ取得パターンの比較

従来の SPA パターン(避けるべき)

データがコンポーネントマウント後にデータ取得される。

ダイアグラムを読み込み中...

コード例

<script lang="ts">
  import { onMount } from 'svelte';
  let data = null;

  onMount(async () => {
    // コンポーネントマウント後にデータ取得
    const response = await fetch('/api/data');
    data = await response.json();
  });
</script>
svelte

SvelteKit の Load 関数パターン(推奨)

データは自動的に実行される load 関数を呼び出し時に fetch にて取得され、その結果をコンポーネントに渡され、データが反映された状態で表示される。

ダイアグラムを読み込み中...

コード例

// +page.ts
export const load = async ({ fetch }) => {
  // ページ表示前にデータ取得
  const response = await fetch('/api/data');
  const data = await response.json();

  return { data };
};
svelte

Load 関数の実行タイミング

ダイアグラムを読み込み中...

2 種類の Load 関数

SvelteKit には 2 種類の Load 関数があります。それぞれ異なる実行環境と用途があり、適切に使い分けることでセキュリティとパフォーマンスを両立できます。

Universal Load (+page.ts / +layout.ts)

サーバーとクライアントの両方で実行される関数です。初回アクセス時は SSR のためサーバーで実行され、クライアントサイドナビゲーション時はブラウザで実行されます。これにより、SEO とユーザー体験の両方を最適化できます。

ダイアグラムを読み込み中...
⚡ 特徴
  • SSR時はサーバーで実行
  • クライアントサイドナビゲーション時はブラウザで実行
  • 公開APIの呼び出しに適している
  • fetchは自動的に最適化される
🎯 適している用途
  • 公開APIの呼び出し
  • URLパラメータの処理
  • クライアント側でも必要なデータ
  • CDNでキャッシュ可能なデータ

基本的な使用例

// +page.ts
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ params, url, fetch }) => {
	// fetch関数はSvelteKitによって拡張され、
	// サーバー/クライアント両方で動作する
	const response = await fetch('/api/data');
	const data = await response.json();

	return {
		data, // このデータはコンポーネントの`data`プロップに渡される
	};
};
typescript

実践的な使用例

// +page.ts
export const load: PageLoad = async ({ fetch, url }) => {
	// ✅ 公開APIの呼び出し
	const posts = await fetch('/api/posts').then((r) => r.json());

	// ✅ URLパラメータの処理
	const page = url.searchParams.get('page') || '1';

	// ✅ クライアントでも必要な計算
	const currentPage = parseInt(page);

	return {
		posts,
		currentPage,
	};
};
typescript

Server Load (+page.server.ts / +layout.server.ts)

サーバーサイドでのみ実行される関数です。データベースへの直接アクセスや秘密情報の取り扱いが可能で、セキュリティを保ちながらバックエンド処理を実装できます。

ダイアグラムを読み込み中...
🔒 特徴
  • 常にサーバーサイドで実行
  • 秘密情報(APIキー、DB認証情報)を安全に扱える
  • データベースに直接アクセス可能
  • Node.js専用のライブラリを使用可能
🎯 適している用途
  • データベースアクセス
  • 認証・認可処理
  • 秘密情報の使用
  • ファイルシステムアクセス
  • サーバーサイド専用ライブラリ

基本的な使用例

// +page.server.ts
import type { PageServerLoad } from './$types';
import { db } from '$lib/server/database';

export const load: PageServerLoad = async ({ params, locals }) => {
	// データベースに直接アクセス可能
	// このコードはブラウザに送られないため安全
	const user = await db.user.findUnique({
		where: { id: params.id },
	});

	return {
		user, // シリアライズ可能なデータのみ返す
	};
};
typescript

実践的な使用例

// +page.server.ts
import { redirect } from '@sveltejs/kit';
import { PRIVATE_API_KEY } from '$env/static/private';
import fs from 'fs/promises';

export const load: PageServerLoad = async ({ cookies, locals, platform }) => {
	// ✅ 認証チェック
	const sessionId = cookies.get('session');
	if (!sessionId) throw redirect(303, '/login');

	// ✅ データベースアクセス
	const user = await db.user.findUnique({
		where: { sessionId },
	});

	// ✅ 秘密情報の使用
	const apiData = await fetch('https://api.example.com', {
		headers: {
			Authorization: `Bearer ${PRIVATE_API_KEY}`,
		},
	});

	// ✅ ファイルシステムアクセス
	const config = await fs.readFile('./config.json', 'utf-8');

	return {
		user,
		apiData: await apiData.json(),
		config: JSON.parse(config),
	};
};
typescript

比較表

項目Universal LoadServer Load
ファイル名+page.ts
+layout.ts
+page.server.ts
+layout.server.ts
型定義PageLoad
LayoutLoad
PageServerLoad
LayoutServerLoad
実行環境サーバー&クライアント両方サーバーのみ
SSR 時サーバーで実行サーバーで実行
CSR 時クライアントで実行サーバーで実行(API 経由)
データベース❌ 直接アクセス不可✅ 直接アクセス可能
秘密情報❌ 扱えない(クライアントに露出)✅ 安全に扱える
Node.js API⚠️ SSR 時のみ✅ 常に利用可能
ファイルシステム⚠️ SSR 時のみ✅ 常に利用可能
実行タイミングナビゲーション前ナビゲーション前
キャッシュクライアント側でキャッシュ可能サーバー側で制御
バンドルサイズクライアントバンドルに含まれる含まれない

Load 関数のパラメータ

共通パラメータ

両方の Load 関数で使用できるパラメータ

パラメータ説明
urlURLリクエストの URL(searchParams を含む)
paramsRecord<string, string>動的ルートパラメータ([slug]など)
route{ id: string }現在のルート ID
fetchFetchSvelteKit 拡張版の fetch 関数
setHeaders(headers: Record<string, string>) => voidレスポンスヘッダーを設定
depends(...deps: string[]) => void依存関係を宣言(invalidate 用)
parent() => Promise<ParentData>親レイアウトのデータを取得

Universal Load 専用パラメータ

パラメータ説明
dataPageDataServer Load からのデータ(ある場合)

Server Load 専用パラメータ

パラメータ説明
requestRequest元の HTTP リクエストオブジェクト
cookiesCookiesCookie の読み書き API
localsApp.LocalsHooks で設定したローカルデータ
platformApp.Platformプラットフォーム固有のデータ
isDataRequestbooleanデータリクエストかどうか
isSubRequestbooleanサブリクエストかどうか

Load 関数の選び方

データ取得の要件に応じて、適切な Load 関数を選択します。

Server Load を使用すべき場合

  1. 秘密情報の取り扱い

    • API キーやデータベースクレデンシャル
    • サードパーティサービスのシークレット
    • 環境変数に保存された機密情報
  2. データベース直接アクセス

    • Prisma、Drizzle ORM などの ORM 使用
    • SQL クエリの直接実行
    • Redis や MongoDB への接続
  3. サーバーサイド専用ライブラリ

    • Node.js 専用モジュールの使用
    • ファイルシステムへのアクセス
    • システムコマンドの実行
// 例:Server Loadが適切なケース
export const load: PageServerLoad = async ({ locals }) => {
	// データベースに直接アクセス
	const posts = await db.post.findMany({
		where: { userId: locals.session.userId },
	});

	// 秘密のAPIキーを使用
	const analytics = await fetchWithSecret(process.env.ANALYTICS_KEY);

	return { posts, analytics };
};
typescript

Universal Load を使用すべき場合

  1. 公開 API の呼び出し

    • REST API エンドポイント
    • GraphQL クエリ
    • パブリック API のデータ
  2. SEO が重要な場合

    • サーバーサイドレンダリングが必要
    • メタデータの動的生成
    • Open Graph タグの設定
  3. クライアントサイド再実行

    • ナビゲーション時のデータ更新
    • リアルタイム更新が必要
    • クライアントサイドキャッシュの活用
// 例:Universal Loadが適切なケース
export const load: PageLoad = async ({ fetch, params }) => {
	// 公開APIからデータ取得
	const response = await fetch(`/api/posts/${params.id}`);
	const post = await response.json();

	// クライアントサイドでも再実行可能
	return { post };
};
typescript

選択フローチャート

ダイアグラムを読み込み中...

async/await の実行タイミングと並列処理

Load 関数内での async/await の実行タイミングと並列処理による最適化の詳細については、 データフローの詳細 ページで解説しています。

TypeScript 型の自動生成システム

SvelteKit の型安全なデータ取得と./$typesの自動生成システムの詳細は、 TypeScript 型の自動生成システム の専用ページで解説しています。

レイアウトとページ間のデータ共有

レイアウトとページ間でデータを共有する方法です。SvelteKit はレイアウトの Load 関数を先に実行し、そのデータをページの Load 関数で利用できるようにします。これにより、共通データの重複取得を避け、効率的なデータフローを実現します。

ダイアグラムを読み込み中...

レイアウトの Load 関数でのデータ取得

レイアウトの Load 関数で取得したデータは、そのレイアウトに属するすべてのページで共有されます。一般的にユーザー情報や設定など、アプリケーション全体で必要なデータをここで取得します。

// +layout.server.ts
import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = async ({ locals }) => {
	// セッションからユーザー情報を取得
	// このデータはすべての子ページで利用可能
	const user = await getUserFromSession(locals.session);

	return {
		user, // このユーザー情報はこのレイアウトに属するすべてのページで利用可能
	};
};
typescript

ページの Load 関数でのデータ取得

ページの Load 関数でparent()関数を使って、上位レイアウトのデータにアクセスします。これにより、レイアウトで取得したデータを基に、追加のデータを取得できます。

// +page.ts
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ parent }) => {
	// 上位レイアウトのデータを取得
	// parent()は上位のLoad関数の完了を待つ
	const { user } = await parent();

	// ユーザー固有のデータを取得
	// 親のデータを活用して追加情報を取得
	const posts = await fetch(`/api/users/${user.id}/posts`).then((r) => r.json());

	return {
		posts,
		// userは自動的に継承される
		// 明示的に返さなくてもコンポーネントで利用可能
	};
};
typescript

実践的な例:EC サイトの商品詳細ページ

実際の EC サイトを想定した、Load 関数の実践的な使用例です。レイアウトでユーザー情報を取得し、ページで商品データを取得する典型的なパターンを示します。

ダイアグラムを読み込み中...

この例では、以下の処理が行われています。

  1. レイアウト Load(+layout.server.ts)

    • Redis からセッション情報を取得
    • ユーザー情報とカート情報を管理
  2. ページ Load(+page.server.ts)

    • parent()で親のユーザー情報を活用
    • PostgreSQL から商品情報を取得
    • AWS S3 から署名付き URL を生成
    • レコメンド商品を取得
  3. Form Action

    • カート追加処理
    • 在庫確認とデータベース更新
    • セッション更新

エラーハンドリング

Load 関数でのエラー処理方法です。適切なエラーハンドリングにより、ユーザーにわかりやすいエラーメッセージを表示し、アプリケーションの信頼性を向上させます。

基本的なエラー処理

SvelteKit のerror関数を使って、HTTP ステータスコードとメッセージを持つエラーを投げます。これにより、適切なエラーページが表示されます。

import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ params }) => {
	const response = await fetch(`/api/posts/${params.id}`);

	if (!response.ok) {
		// HTTPエラーを投げる
		// error関数はステータスコードとオプションデータを受け取る
		throw error(response.status, {
			message: 'Post not found', // エラーページで表示されるメッセージ
		});
	}

	const post = await response.json();
	return { post };
};
typescript

カスタムエラーページ

+error.svelteファイルを作成して、エラーの表示をカスタマイズします。このファイルは Load 関数でエラーが発生したときに自動的に表示されます。

<!-- +error.svelte(SvelteKit 2.12+ 推奨パターン) -->
<script lang="ts">
  import { page } from '$app/state';
  // $app/state からエラー情報にアクセス($プレフィックス不要)
</script>

<!-- HTTPステータスコードを表示 -->
<h1>{page.status}</h1>
<!-- エラーメッセージを表示 -->
<p>{page.error?.message}</p>
svelte
$app/state vs $app/stores(SvelteKit 2.12+)

SvelteKit 2.12 以降では、$app/stateが推奨されます。

// ✅ 推奨(SvelteKit 2.12+)
import { page } from '$app/state';
// $プレフィックスなしで直接アクセス
console.log(page.url.pathname);

// 従来の方法(引き続き動作)
import { page } from '$app/stores';
// ストアなので$プレフィックスが必要
console.log($page.url.pathname);
typescript

$app/stateの利点

  • Svelte 5 の runes システムとの一貫性
  • $プレフィックス不要でコードがすっきり
  • TypeScript の型推論が改善

よく使われるパターン

実際のアプリケーションでよく使われる Load 関数のパターンを紹介します。これらの例を参考に、効率的なデータ取得を実装できます。

複数データの同時取得

複数のデータソースから同時にデータを取得することで、パフォーマンスを大幅に改善できます。Promise.all を使って並列処理を実現します。

export const load: PageLoad = async ({ fetch }) => {
	// 並列でデータを取得(効率的)
	// 3つのAPIコールが同時に実行される
	const [posts, categories, tags] = await Promise.all([
		fetch('/api/posts').then((r) => r.json()),
		fetch('/api/categories').then((r) => r.json()),
		fetch('/api/tags').then((r) => r.json()),
	]);

	return {
		posts, // 投稿データ
		categories, // カテゴリ一覧
		tags, // タグ一覧
	};
};
typescript

動的なデータ取得

URL パラメータやユーザーの状態に応じて、異なるデータを取得するパターンです。動的なコンテンツ表示に役立ちます。

export const load: PageLoad = async ({ url, fetch }) => {
	const filter = url.searchParams.get('filter');

	// 条件に応じて異なるデータを取得
	// ?filter=active のようなクエリパラメータをチェック
	const data = filter
		? await fetch(`/api/items?filter=${filter}`).then((r) => r.json())
		: await fetch('/api/items').then((r) => r.json());

	return {
		items: data, // フィルタリングされたデータまたは全データ
	};
};
typescript

ベストプラクティス

Load 関数を使う際の推奨事項です。これらのベストプラクティスに従うことで、保守性が高くパフォーマントなコードを書けます。

  1. エラーハンドリング

    • 適切な HTTP ステータスコードを返す
    • ユーザーフレンドリーなエラーメッセージ
  2. キャッシュを考慮

    • fetchcacheオプションを適切に設定
    • 適切なキャッシュヘッダーの設定
  3. パフォーマンス最適化

    • 並列処理でデータ取得を高速化
    • 不要なデータ取得を避ける

次のステップ

Last update at: 2026/01/09 18:10:16