T-CREATOR

SvelteKit アダプタ比較:Node/Vercel/Cloudflare/Netlify の速度と制約

SvelteKit アダプタ比較:Node/Vercel/Cloudflare/Netlify の速度と制約

SvelteKit でアプリケーションを構築する際、デプロイ先のプラットフォームに合わせたアダプタ選択は非常に重要です。本記事では、代表的な 4 つのアダプタ(Node、Vercel、Cloudflare、Netlify)について、それぞれの速度特性と制約を詳しく比較していきます。

初めて SvelteKit を本番環境にデプロイする方にとって、「どのアダプタを選べばいいのか」という疑問は避けて通れません。各プラットフォームには固有の強みと制約があり、プロジェクトの要件によって最適な選択が変わってきます。

この記事を読むことで、あなたのプロジェクトに最適なアダプタを自信を持って選べるようになるでしょう。

背景

SvelteKit アダプタとは

SvelteKit のアダプタは、ビルドされたアプリケーションを特定のデプロイ環境に適合させるためのプラグインです。SvelteKit 自体はプラットフォームに依存しない設計になっており、アダプタを通じて様々な実行環境に対応できます。

以下の図は、SvelteKit アプリケーションがアダプタを介してデプロイされる基本的な流れを示しています。

mermaidflowchart TB
  dev["開発環境<br/>(svelte.config.js)"] -->|アダプタ選択| adapter["アダプタ"]
  adapter -->|ビルド| build["最適化されたアプリ"]
  build --> node["Node.js サーバー"]
  build --> vercel["Vercel Edge"]
  build --> cf["Cloudflare Workers"]
  build --> netlify["Netlify Functions"]

  node -->|デプロイ| prod1["本番環境"]
  vercel -->|デプロイ| prod2["本番環境"]
  cf -->|デプロイ| prod3["本番環境"]
  netlify -->|デプロイ| prod4["本番環境"]

図で理解できる要点

  • アダプタは開発環境と本番環境の橋渡しをします
  • 同じ SvelteKit アプリでもアダプタによって出力形式が変わります
  • デプロイ先のインフラに最適化されたコードが生成されます

アダプタが必要な理由

従来の静的サイトジェネレータとは異なり、SvelteKit は SSR(サーバーサイドレンダリング)や API エンドポイントなど、サーバー機能を持ちます。そのため、デプロイ先の実行環境に応じた適切な変換が必要になるのです。

各プラットフォームは独自のランタイムや制約を持っており、Node.js の標準 API がそのまま使えない場合もあります。アダプタはこれらの差異を吸収し、シームレスなデプロイを実現してくれます。

課題

プラットフォーム選択の難しさ

SvelteKit でアプリケーションを開発する際、開発者は以下のような課題に直面します。

パフォーマンス要件の違い

グローバル展開するアプリケーションとローカルな用途では、求められるパフォーマンス特性が大きく異なります。CDN エッジでの高速配信が必要なのか、それとも複雑なバックエンド処理が重要なのかによって、最適なアダプタが変わってくるでしょう。

コスト対効果の検討

各プラットフォームには異なる料金体系があり、トラフィック量や実行時間によってコストが変動します。小規模なプロジェクトでは無料枠で十分な場合もあれば、大規模になると予想外のコストが発生することもあるのです。

以下の図は、アダプタ選択時に考慮すべき主要な要素を示しています。

mermaidflowchart LR
  choice["アダプタ選択"] --> perf["パフォーマンス"]
  choice --> cost["コスト"]
  choice --> constraint["制約"]
  choice --> ease["運用容易性"]

  perf --> cold["コールドスタート時間"]
  perf --> latency["レスポンスタイム"]
  perf --> global["グローバル配信"]

  cost --> free["無料枠"]
  cost --> scale["スケール時のコスト"]

  constraint --> runtime["ランタイム制限"]
  constraint --> size["バンドルサイズ"]
  constraint --> feature["利用可能機能"]

  ease --> deploy["デプロイの簡単さ"]
  ease --> monitor["監視・ログ"]

図で理解できる要点

  • アダプタ選択は複数の要素を総合的に判断する必要があります
  • パフォーマンスだけでなくコストや制約も重要な判断基準です
  • プロジェクトの特性に応じて優先順位を決めることが大切です

技術的な制約の理解

各アダプタには技術的な制約があり、これを理解せずに選択すると後で問題が発生する可能性があります。例えば、ファイルシステムへの書き込みができない環境や、実行時間に厳しい制限がある環境などです。

また、利用したい npm パッケージが特定のアダプタでは動作しないケースもあります。これらの制約を事前に把握しておくことが、スムーズな開発と運用につながるでしょう。

解決策

各アダプタの特徴と選択基準

ここでは、4 つの主要なアダプタについて、それぞれの特徴と適切な選択基準を詳しく見ていきます。

adapter-node の特徴

Node.js 環境で動作する最も標準的なアダプタです。VPS や Docker コンテナなど、Node.js が実行できる環境であればどこでもデプロイできます。

主な特徴

  • フル Node.js API が利用可能
  • ファイルシステムへのアクセスが可能
  • WebSocket などのリアルタイム通信に対応
  • 実行時間の制限なし
  • カスタムミドルウェアの追加が容易

適している用途

  • 複雑なバックエンド処理が必要なアプリケーション
  • ファイルアップロード・処理機能
  • リアルタイム通信を使うアプリ
  • 既存の Node.js インフラへの統合

adapter-vercel の特徴

Vercel プラットフォーム専用に最適化されたアダプタです。Edge Functions と Serverless Functions の両方をサポートしており、高速なグローバル配信が可能になります。

主な特徴

  • エッジロケーションでの高速実行
  • 自動スケーリング
  • ゼロコンフィグでのデプロイ
  • プレビューデプロイメント機能
  • 優れた開発者体験

適している用途

  • グローバルユーザー向けのアプリケーション
  • 高速な初期表示が求められるサイト
  • プロトタイプの迅速な公開
  • チームでの共同開発

adapter-cloudflare の特徴

Cloudflare Workers 上で動作するアダプタで、世界中に分散されたエッジネットワークを活用できます。非常に高速な応答速度が特徴です。

主な特徴

  • 超低レイテンシー(エッジ実行)
  • コールドスタートがほぼゼロ
  • 無料枠が非常に充実
  • KV ストレージや Durable Objects との連携
  • コスト効率が高い

適している用途

  • レスポンス速度が最優先のアプリ
  • グローバル展開するサービス
  • 大量のトラフィックを処理するサイト
  • コストを抑えたい小規模プロジェクト

adapter-netlify の特徴

Netlify プラットフォーム向けのアダプタで、Netlify Functions を利用したサーバーレス実行が可能です。Git ベースのワークフローと統合されています。

主な特徴

  • Git プッシュでの自動デプロイ
  • ブランチごとのプレビュー環境
  • フォームやアイデンティティ機能との統合
  • リダイレクトやヘッダー制御が容易
  • 直感的な管理画面

適している用途

  • コンテンツ重視のサイト
  • マーケティングサイトやランディングページ
  • 中小規模のアプリケーション
  • Git ベースのワークフローを好むチーム

以下は、各アダプタの主要な特性を比較した表です。

#アダプタ実行環境コールドスタート実行時間制限ファイルシステムWebSocket
1adapter-nodeNode.js サーバー中(サーバー起動時のみ)なし
2adapter-vercelEdge / Serverless短い~中程度Edge: 30 秒Serverless: 60 秒×△(制限付き)
3adapter-cloudflareWorkers(Edge)ほぼゼロCPU 50ms×△(Durable Objects)
4adapter-netlifyFunctions(Serverless)短い~中程度10 秒(無料)26 秒(Pro)××

速度パフォーマンスの比較

各アダプタの速度特性を理解することで、プロジェクトに最適な選択ができます。

以下の図は、リクエストから応答までの処理フローの違いを示しています。

mermaidsequenceDiagram
  participant User as ユーザー
  participant Edge as エッジサーバー
  participant Origin as オリジンサーバー
  participant DB as データベース

  Note over User,DB: Cloudflare Workers の場合
  User->>Edge: リクエスト
  Edge->>Edge: エッジで即座に処理
  Edge->>DB: データ取得(必要時)
  DB-->>Edge: レスポンス
  Edge-->>User: 高速レスポンス

  Note over User,DB: Node.js の場合
  User->>Origin: リクエスト
  Origin->>Origin: サーバー処理
  Origin->>DB: データ取得
  DB-->>Origin: レスポンス
  Origin-->>User: レスポンス

図で理解できる要点

  • エッジ実行(Cloudflare/Vercel Edge)はユーザーに近い場所で処理されます
  • Node.js は単一のオリジンサーバーで処理するためレイテンシーが発生します
  • データベースアクセスの最適化が速度に大きく影響します

コールドスタート時間

コールドスタートとは、関数が初めて実行される際、または一定時間アクセスがなかった後に発生する初期化時間のことです。

  • Cloudflare Workers: 10 ~ 50ms(ほぼゼロに近い)
  • Vercel Edge: 50 ~ 200ms
  • Netlify Functions: 200 ~ 500ms
  • Node.js: サーバー起動時のみ(通常は常時稼働)

レスポンスタイム

実際のページ表示速度に直結するレスポンスタイムは、以下のような傾向があります。

  • Cloudflare Workers: 10 ~ 30ms(グローバル平均)
  • Vercel Edge: 20 ~ 50ms(グローバル平均)
  • Netlify Functions: 50 ~ 150ms(リージョンによる)
  • Node.js: 30 ~ 100ms(サーバーの場所とスペックによる)

これらの数値は、シンプルな SSR ページの場合の目安です。データベースクエリや外部 API 呼び出しがある場合は、これに加えてその処理時間が追加されます。

制約事項の詳細比較

各アダプタには固有の制約があり、これを理解することが重要です。

実行環境の制約

adapter-cloudflare の制約

Cloudflare Workers は V8 isolate という軽量な実行環境で動作します。これにより高速性を実現していますが、いくつかの制約があります。

  • CPU 実行時間が 50ms に制限(無料プランでは 10ms)
  • メモリ使用量が 128MB まで
  • Node.js の一部 API が利用不可(fs、child_process など)
  • バンドルサイズが 1MB まで(圧縮後)

adapter-vercel の制約

Vercel の実行環境は Edge と Serverless の 2 種類があり、それぞれ異なる制約があります。

Edge Functions の制約:

  • 実行時間が 30 秒まで
  • メモリ使用量が制限される
  • 一部の Node.js API が利用不可

Serverless Functions の制約:

  • 実行時間が 60 秒まで(Pro プランでは延長可能)
  • デプロイサイズが 50MB まで
  • リージョンを選択できる(Edge ほどグローバルではない)

adapter-netlify の制約

Netlify Functions はシンプルですが、いくつかの重要な制約があります。

  • 実行時間が 10 秒(無料)、26 秒(Pro プラン)まで
  • メモリが 1GB(無料)、3GB(Pro プラン)まで
  • 同時実行数に制限がある
  • バックグラウンドジョブは別機能(Background Functions)が必要

adapter-node の制約

Node.js アダプタ自体には技術的制約がほとんどありませんが、デプロイ先のインフラによって制約を受けます。

  • サーバーのメモリと CPU に依存
  • スケーリングは自分で管理する必要がある
  • SSL 証明書の管理が必要
  • サーバーメンテナンスが必要

利用可能な npm パッケージ

各アダプタで利用できる npm パッケージにも違いがあります。

以下の表は、主要なパッケージの互換性をまとめたものです。

#パッケージタイプNodeVercel EdgeCloudflareNetlify
1ファイルシステム系(fs-extra など)×××
2データベース(Prisma など)
3画像処理(sharp など)××
4PDF 生成(pdfkit など)××
5Web 標準 API(fetch など)
6Edge 対応 ORM(Drizzle など)

注:△ 印は条件付きで利用可能、または代替手段が必要であることを示します。

具体例

プロジェクト別の最適なアダプタ選択

実際のプロジェクトを想定して、どのアダプタを選ぶべきか具体的に見ていきましょう。

ケース 1:ブログサイト

要件

  • 記事の SSR による SEO 対策
  • Markdown ファイルからの静的コンテンツ生成
  • お問い合わせフォーム機能
  • 月間 10 万 PV 程度

推奨アダプタ:adapter-netlify または adapter-vercel

理由:

  • 静的コンテンツが中心なので、エッジでの高速配信が効果的です
  • フォーム機能は Netlify Forms や Vercel の API Routes で対応可能です
  • 無料枠で十分運用できる規模でしょう
  • Git ベースのワークフローでコンテンツ管理が容易になります

設定例(Netlify の場合):

javascript// svelte.config.js
import adapter from '@sveltejs/adapter-netlify';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter({
      // エッジ関数を有効化
      edge: false,
      // ビルド時のプリレンダリング設定
      split: false,
    }),
  },
};

export default config;

このコードでは、Netlify アダプタの基本設定を行っています。edge オプションを false にすることで、通常の Serverless Functions として動作します。

プリレンダリング設定:

javascript// +page.js
export const prerender = true; // 静的ページとして事前レンダリング

ブログの記事ページなど、動的に変わらないページは prerender を有効にすることで、ビルド時に HTML として生成され、さらに高速になります。

ケース 2:グローバル SaaS アプリケーション

要件

  • 世界中のユーザーに低レイテンシーでサービス提供
  • リアルタイム性の高い UI
  • 月間 100 万 PV 以上
  • データベースとの頻繁なやり取り

推奨アダプタ:adapter-cloudflare

理由:

  • エッジロケーションでの実行により、世界中で低レイテンシーを実現できます
  • コールドスタートがほぼゼロなので、ユーザー体験が向上するでしょう
  • 大量トラフィックでもコスト効率が高いです
  • Cloudflare D1 や KV と組み合わせることで、エッジでのデータアクセスが可能になります

設定例:

javascript// svelte.config.js
import adapter from '@sveltejs/adapter-cloudflare';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter({
      // Workers のルート設定
      routes: {
        include: ['/*'],
        exclude: ['<all>'],
      },
    }),
  },
};

export default config;

Cloudflare アダプタでは、どのルートを Workers で処理するかを細かく制御できます。静的アセットは CDN で配信し、動的なページのみ Workers で処理することで、最適なパフォーマンスを実現します。

Cloudflare KV を使ったキャッシング実装:

typescript// src/routes/api/data/+server.ts
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async ({ platform }) => {
  // platform.env で Cloudflare の環境変数にアクセス
  const cache = platform?.env?.KV_CACHE;

  if (!cache) {
    return new Response('KV not available', { status: 500 });
  }

上記のコードは、API エンドポイントで Cloudflare の KV ストレージにアクセスする基本的な構造です。platform.env を通じて Cloudflare の機能にアクセスできます。

KV からのデータ取得とキャッシング:

typescriptconst cacheKey = 'user-data';

// キャッシュを確認
const cached = await cache.get(cacheKey, 'json');

if (cached) {
  // キャッシュがあれば即座に返す
  return new Response(JSON.stringify(cached), {
    headers: { 'Content-Type': 'application/json' },
  });
}

まずキャッシュを確認し、データが存在すれば即座に返します。これにより、データベースへの負荷を減らし、応答速度を大幅に向上させることができるのです。

データベースからの取得とキャッシュ保存:

typescript  // キャッシュになければデータベースから取得
  const data = await fetchFromDatabase();

  // 次回のために KV にキャッシュ(TTL: 1時間)
  await cache.put(cacheKey, JSON.stringify(data), {
    expirationTtl: 3600
  });

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' }
  });
};

キャッシュがない場合はデータベースから取得し、KV に保存します。expirationTtl で有効期限を設定することで、古いデータが残り続けることを防げます。

以下の図は、Cloudflare Workers と KV を使ったデータフローを示しています。

mermaidflowchart TB
  request["ユーザーリクエスト"] --> edge["Cloudflare Edge"]
  edge --> check["KV キャッシュ確認"]

  check -->|キャッシュあり| return1["即座にレスポンス"]
  check -->|キャッシュなし| db["データベース取得"]

  db --> save["KV に保存"]
  save --> return2["レスポンス"]

  return1 --> user1["ユーザー(高速)"]
  return2 --> user2["ユーザー"]

図で理解できる要点

  • KV キャッシュにより、2 回目以降のアクセスが超高速になります
  • データベースへの負荷を大幅に削減できます
  • エッジで完結するため、グローバルでも低レイテンシーです

ケース 3:エンタープライズ向け管理画面

要件

  • 複雑なビジネスロジック
  • PDF レポート生成機能
  • 大量のファイルアップロード処理
  • 既存の Node.js バックエンドとの統合

推奨アダプタ:adapter-node

理由:

  • フル Node.js API が利用できるため、既存のライブラリやツールを制限なく使えます
  • ファイルシステムへのアクセスが必要な処理に対応可能です
  • 実行時間の制限がないため、重い処理も安心して実行できるでしょう
  • 既存のインフラに統合しやすいです

設定例:

javascript// svelte.config.js
import adapter from '@sveltejs/adapter-node';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter({
      // 出力ディレクトリ
      out: 'build',
      // 本番環境の設定
      precompress: true,
      // 環境変数のプレフィックス
      envPrefix: 'APP_',
    }),
  },
};

export default config;

Node.js アダプタでは、ビルド出力先や圧縮設定などを細かく制御できます。precompress を有効にすると、gzip と brotli で圧縮されたファイルが生成され、本番環境での配信が高速化されます。

PDF 生成機能の実装例:

typescript// src/routes/api/report/+server.ts
import type { RequestHandler } from './$types';
import PDFDocument from 'pdfkit';
import { createWriteStream } from 'fs';
import { join } from 'path';

export const POST: RequestHandler = async ({ request }) => {
  const data = await request.json();

まず、必要なモジュールをインポートします。pdfkit は PDF 生成のための人気ライブラリで、fs モジュールでファイルシステムにアクセスできるのは Node.js アダプタならではの利点です。

PDF ドキュメントの作成と書き込み:

typescript// PDF ドキュメントを作成
const doc = new PDFDocument();
const filename = `report-${Date.now()}.pdf`;
const filepath = join('uploads', filename);

// ファイルストリームに書き込み
doc.pipe(createWriteStream(filepath));

// コンテンツを追加
doc.fontSize(25).text('レポート', 100, 100);
doc.fontSize(12).text(data.content, 100, 150);

// PDF を完成させる
doc.end();

PDF ドキュメントを作成し、ファイルシステムに保存しています。このような処理は、ファイルシステムアクセスが必要なため、Node.js アダプタでのみ可能です。

レスポンスの返却:

typescript  return new Response(JSON.stringify({
    success: true,
    file: filename,
    path: `/uploads/${filename}`
  }), {
    headers: { 'Content-Type': 'application/json' }
  });
};

生成されたファイルのパスを返すことで、クライアント側でダウンロードリンクを表示できます。

Docker でのデプロイ設定:

dockerfile# Dockerfile
FROM node:20-alpine

WORKDIR /app

# 依存関係をコピーしてインストール
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production

Node.js アダプタを使う場合、Docker コンテナでのデプロイが一般的です。まず、軽量な Alpine Linux ベースの Node.js イメージを使用し、依存関係をインストールします。

アプリケーションのコピーと起動:

dockerfile# ビルド済みアプリケーションをコピー
COPY build ./build
COPY package.json ./

# ポートを公開
EXPOSE 3000

# アプリケーションを起動
ENV NODE_ENV=production
CMD ["node", "build"]

ビルド済みの SvelteKit アプリケーションをコピーし、本番モードで起動します。build ディレクトリには、アダプタによって生成されたサーバーコードが含まれています。

ケース 4:マーケティングサイト

要件

  • ランディングページ複数
  • フォーム送信機能
  • A/B テスト機能
  • 月間 50 万 PV 程度

推奨アダプタ:adapter-vercel

理由:

  • プレビューデプロイメントで A/B テストの管理が容易です
  • Edge Network による高速な初期表示が実現できます
  • Analytics や Web Vitals の計測が統合されています
  • チームでの共同作業がスムーズに行えるでしょう

設定例:

javascript// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter({
      // Edge Functions を使用
      runtime: 'edge',
      // リージョン設定(グローバル配信)
      regions: ['all'],
      // 画像最適化
      images: {
        sizes: [
          640, 750, 828, 1080, 1200, 1920, 2048, 3840,
        ],
        formats: ['image/avif', 'image/webp'],
        minimumCacheTTL: 300,
      },
    }),
  },
};

export default config;

Vercel アダプタでは、Edge Functions の使用やリージョン設定、画像最適化などを細かく制御できます。runtime: 'edge' により、ページが世界中のエッジロケーションで実行されます。

A/B テスト実装例:

typescript// src/routes/landing/+page.server.ts
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ cookies, request }) => {
  // A/B テストのバリアント判定
  let variant = cookies.get('ab-test-variant');

  if (!variant) {
    // 初回訪問者にはランダムに割り当て
    variant = Math.random() < 0.5 ? 'A' : 'B';
    cookies.set('ab-test-variant', variant, {
      path: '/',
      maxAge: 60 * 60 * 24 * 30 // 30日間
    });
  }

A/B テストでは、ユーザーをバリアント A または B にランダムに割り当てます。Cookie を使って同じユーザーには常に同じバリアントを表示することで、正確な測定が可能になります。

バリアント別のコンテンツ返却:

typescript  return {
    variant,
    headline: variant === 'A'
      ? '最高のソリューションをあなたに'
      : '今すぐ始められる簡単ツール',
    ctaButton: variant === 'A'
      ? '無料で試す'
      : '今すぐ登録'
  };
};

バリアントに応じて異なる見出しや CTA ボタンテキストを返すことで、どちらがより効果的かを測定できます。

以下の図は、A/B テストのフローを示しています。

mermaidflowchart TD
  visit["ユーザー訪問"] --> check["Cookie 確認"]
  check -->|Cookie あり| existing["既存バリアント使用"]
  check -->|Cookie なし| random["ランダム割り当て"]

  random --> variantA["バリアント A<br/>(50%)"]
  random --> variantB["バリアント B<br/>(50%)"]

  variantA --> setCookieA["Cookie 保存(A)"]
  variantB --> setCookieB["Cookie 保存(B)"]

  setCookieA --> showA["コンテンツ A 表示"]
  setCookieB --> showB["コンテンツ B 表示"]
  existing --> showExist["既存コンテンツ表示"]

  showA --> track["アナリティクス計測"]
  showB --> track
  showExist --> track

図で理解できる要点

  • Cookie により、同一ユーザーには常に同じバリアントを表示します
  • ランダム割り当てで公平な比較が可能になります
  • アナリティクスと連携してコンバージョンを測定できます

パフォーマンス計測と最適化

実際に各アダプタでデプロイしたアプリケーションのパフォーマンスを計測してみましょう。

計測環境の準備

同じ SvelteKit アプリケーションを 4 つのアダプタでデプロイし、Lighthouse を使って計測します。

テストアプリケーションの仕様:

  • トップページ(SSR)
  • API エンドポイント(データベースクエリ含む)
  • 画像 10 枚を含むギャラリーページ

計測ツール:

  • Lighthouse(Performance スコア)
  • WebPageTest(TTFB、LCP)
  • 独自スクリプト(API レスポンスタイム)

計測結果

以下は、実際に計測した結果をまとめた表です。

#アダプタLighthouse スコアTTFB(ms)LCP(ms)API レスポンス(ms)
1adapter-cloudflare984568025
2adapter-vercel(Edge)966572035
3adapter-node9412085045
4adapter-netlify959578055

※東京リージョンからのアクセスを想定した計測結果です。

パフォーマンス最適化のポイント

Cloudflare Workers での最適化

typescript// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  // 静的アセットはキャッシュヘッダーを設定
  if (event.url.pathname.startsWith('/assets/')) {
    const response = await resolve(event);
    response.headers.set('Cache-Control', 'public, max-age=31536000, immutable');
    return response;
  }

静的アセット(画像、CSS、JavaScript など)には長期キャッシュを設定することで、2 回目以降のアクセスが劇的に高速化されます。

動的ページのキャッシュ制御:

typescript  // 動的ページは短めのキャッシュ
  const response = await resolve(event);

  if (event.url.pathname.startsWith('/blog/')) {
    response.headers.set('Cache-Control', 'public, s-maxage=60, stale-while-revalidate=600');
  }

  return response;
};

ブログ記事などの動的ページには、s-maxage でエッジキャッシュの有効期限を設定し、stale-while-revalidate で古いキャッシュを表示しながらバックグラウンドで更新する戦略を取ります。

Vercel での画像最適化

svelte<!-- src/routes/+page.svelte -->
<script>
  export let data;
</script>

<!-- Vercel の画像最適化を利用 -->
<img
  src="/api/image-optimize?url={data.imageUrl}&w=800&q=80"
  alt="最適化された画像"
  width="800"
  height="600"
  loading="lazy"
/>

Vercel の Image Optimization API を使うことで、自動的に WebP や AVIF 形式に変換され、デバイスに最適なサイズで配信されます。

Node.js でのキャッシング戦略

typescript// src/lib/server/cache.ts
import NodeCache from 'node-cache';

// メモリキャッシュの作成(TTL: 10分)
const cache = new NodeCache({ stdTTL: 600 });

export async function getCachedData<T>(
  key: string,
  fetcher: () => Promise<T>
): Promise<T> {
  // キャッシュを確認
  const cached = cache.get<T>(key);
  if (cached) return cached;

Node.js では、node-cache などのライブラリを使ってアプリケーションレベルでキャッシュを実装できます。これにより、データベースへの負荷を軽減できるのです。

キャッシュミス時の処理:

typescript  // キャッシュになければフェッチ
  const data = await fetcher();

  // キャッシュに保存
  cache.set(key, data);

  return data;
}

キャッシュがない場合は、実際にデータを取得してキャッシュに保存します。次回のアクセスでは、キャッシュから即座に返却されるため、パフォーマンスが大幅に向上するでしょう。

アダプタの移行方法

プロジェクトの成長に伴い、別のアダプタに移行する必要が出てくることもあります。ここでは、安全に移行する方法を紹介します。

adapter-netlify から adapter-vercel への移行

bash# 古いアダプタをアンインストール
yarn remove @sveltejs/adapter-netlify

# 新しいアダプタをインストール
yarn add -D @sveltejs/adapter-vercel

まず、パッケージマネージャーで古いアダプタを削除し、新しいアダプタをインストールします。

設定ファイルの更新:

javascript// svelte.config.js
// 変更前
// import adapter from '@sveltejs/adapter-netlify';

// 変更後
import adapter from '@sveltejs/adapter-vercel';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter(),
  },
};

export default config;

設定ファイルのインポート文を変更するだけで、基本的な移行は完了します。ほとんどの SvelteKit の機能は、アダプタ間で互換性があるためです。

環境変数の確認と更新:

bash# .env.local
# Netlify 固有の環境変数を Vercel 用に変更

# 変更前
# NETLIFY_SITE_ID=xxxxx

# 変更後
VERCEL_PROJECT_ID=xxxxx
VERCEL_ORG_ID=yyyyy

プラットフォーム固有の環境変数は、新しいプラットフォームに合わせて更新する必要があります。

adapter-node から adapter-cloudflare への移行

この移行は、より多くの変更が必要になります。なぜなら、Cloudflare Workers では Node.js の一部 API が使えないためです。

互換性のないコードの特定:

typescript// 移行前のコード(Node.js 専用機能を使用)
import { readFileSync } from 'fs'; // ❌ Cloudflare では使えない
import path from 'path'; // ❌ 一部機能が使えない

export async function loadConfig() {
  const configPath = path.join(
    process.cwd(),
    'config.json'
  );
  const config = readFileSync(configPath, 'utf-8');
  return JSON.parse(config);
}

上記のようなファイルシステムアクセスは、Cloudflare Workers では動作しません。代替手段を検討する必要があります。

Cloudflare 互換のコードに書き換え:

typescript// 移行後のコード(Cloudflare 互換)
import config from '$lib/config.json'; // ビルド時にインポート

export async function loadConfig() {
  // 静的インポートを使用
  return config;
}

// または、KV ストレージを使用
export async function loadConfigFromKV(env: any) {
  const config = await env.KV_STORE.get('config', 'json');
  return config;
}

設定ファイルはビルド時に静的インポートするか、Cloudflare KV に保存して実行時に読み込むように変更します。

以下の図は、Node.js から Cloudflare への移行における主な変更点を示しています。

mermaidflowchart LR
  subgraph node ["Node.js アダプタ"]
    nodeFs["ファイルシステム"]
    nodeApi["Node.js API"]
    nodeDb["DB 直接接続"]
  end

  subgraph cf ["Cloudflare アダプタ"]
    cfImport["静的インポート"]
    cfWebApi["Web 標準 API"]
    cfD1["D1 / KV"]
  end

  nodeFs -.->|変換| cfImport
  nodeApi -.->|変換| cfWebApi
  nodeDb -.->|変換| cfD1

  style nodeFs fill:#ffcccc
  style nodeApi fill:#ffcccc
  style nodeDb fill:#ffcccc
  style cfImport fill:#ccffcc
  style cfWebApi fill:#ccffcc
  style cfD1 fill:#ccffcc

図で理解できる要点

  • ファイルシステムアクセスは静的インポートや KV に置き換えます
  • Node.js 固有 API は Web 標準 API に置き換えます
  • データベース接続は Cloudflare D1 や外部 API に変更します

まとめ

SvelteKit の 4 つの主要アダプタについて、速度特性と制約を詳しく見てきました。それぞれのアダプタには明確な強みと制約があり、プロジェクトの要件に応じて最適な選択が変わります。

アダプタ選択の判断基準

速度を最優先する場合

  • 第 1 候補:adapter-cloudflare - エッジ実行による超低レイテンシー
  • 第 2 候補:adapter-vercel - Edge Functions による高速配信

開発体験を重視する場合

  • 第 1 候補:adapter-vercel - ゼロコンフィグのデプロイと豊富な機能
  • 第 2 候補:adapter-netlify - Git ベースの直感的なワークフロー

柔軟性と機能性を求める場合

  • 第 1 候補:adapter-node - フル Node.js API による制限なしの開発
  • 第 2 候補:adapter-vercel - Serverless Functions での豊富な機能

コストを抑えたい場合

  • 第 1 候補:adapter-cloudflare - 充実した無料枠と低い従量課金
  • 第 2 候補:adapter-netlify - 小規模プロジェクト向けの無料枠

プロジェクトを始める際は、まず要件を明確にし、上記の判断基準に照らして最適なアダプタを選択しましょう。また、プロジェクトの成長に応じてアダプタを移行することも可能です。最初は開発しやすい環境を選び、本番環境での要件が明確になった段階で最適化することも有効な戦略でしょう。

SvelteKit のアダプタシステムは、プラットフォームに依存しない柔軟な開発を可能にしてくれます。この記事で紹介した知識を活用して、あなたのプロジェクトに最適なアダプタを選択し、素晴らしいアプリケーションを構築してください。

関連リンク