4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SvelteKitでDynamoDBからデータを取得してSSGする

Last updated at Posted at 2025-12-16

この記事はRust+SvelteKit+CDK で RSS 要約アプリを作ってみる Advent Calendar 2025の 17 日目の記事になります。

また、筆者が属している株式会社野村総合研究所のアドベントカレンダーもあるので、ぜひ購読ください。

はじめに

SSG(静的サイトジェネレーター)のメリットに「あらかじめデータがレンダリングされているので表示が高速」であることが挙げられます。しかし、そのためには「ビルド時にデータを取得して HTML に焼き込む」という工程が必要になります。
SvelteKit でこれを実現するために、今回は「ビルド前スクリプトでデータを JSON 化し、それを読み込む」というアプローチを紹介します。

動的パスを含む SSG で気をつけること

動的部分の決定

動的パス(例:/blog/[slug]のような URL 設計で、[slug]の部分が動的である)で定義されるルートが存在する場合、そのルートの prerender(SSG においてあらかじめレンダリングすること)の実装が難しくなります。
オンデマンドでレンダリングする場合は、リクエスト時点でその[slug]の値が決まっているのですが、SSG のタイミングではどのような値が[slug]に入りうるか決まっていないためです。

Sveltekit ではそれを決定するentriesという機能を持っています。この機能を使うことで、SSG の際にどのような[slug]に対して prerender すればいいのかを定義することができます。

ISG でない辛さ

SvelteKit は、執筆時点では ISG (Incremental Static Generation) に対応していません(そのはず)。ISG を使うと、初回アクセスされたルートはオンデマンドでレンダリングされますが、その結果が静的に保存されるため、2回目以降に同じルートにアクセスした際は SSG したときと同じような挙動となります。これが使えればいいとこどりではあったのですが、その機能はありませんでした。

また、今回動的に生成する部分は日付ごとになるのですが、「どの日付からの範囲で生成するのか」をどのように決定するのかがポイントでした。DB に持つ、ファイルに持つ、などが考えられましたが、今回は「S3 のパスを見る」方法で解決しました。ビルドされた成果物は S3 に日付ごとに配置されるので、そのファイルパスを走査すれば、最も古い日付がわかるため、そこの日付を基準日として扱えます。

SumaRSS での実装

動的ルートのパラメータ定義(entries

web/src/routes/blog/[slug]/+page.server.tsにおいて、entries関数をエクスポートします。
これは、動的ルート([slug])が取りうる値を配列として返す関数で、adapter-staticはこれを元にページを生成します。

// web/src/routes/blog/[slug]/+page.server.ts

import { getDatesToBuild } from "$lib/build";
import type { EntryGenerator, PageServerLoad } from "./$types";
import { listArticlesByDate } from "$lib/articles";

// 生成するページのパラメータ(slug=日付)を返す
export const entries: EntryGenerator = async () => {
  // S3から既存の日付を取得し、今日の日付などを追加して返す
  return (await getDatesToBuild()).map((date) => ({
    slug: date,
  }));
};

getDatesToBuild関数(web/src/lib/build.ts)は、S3 上の既存の日付フォルダをリストアップし、それに「今日」を加えたリストを返します。これにより、過去の記事ページも再生成され、新規記事も反映されます。

ページごとのデータ取得(load

各ページのデータは、通常の SvelteKit アプリと同様にload関数で取得します。
SSG(prerender = true)の場合、このload関数はビルド時にのみ実行されます(AWS へのアクセスもビルド時のみ)。

// web/src/routes/blog/[slug]/+page.server.ts

export const load: PageServerLoad = async ({ params }) => {
  const { slug } = params;
  // DynamoDBからその日付の記事一覧を取得
  const articles = await listArticlesByDate(slug);

  // 要約済みの記事のみを表示対象とする
  return {
    articles: articles.filter((a) => a.state === "Summarized"),
    date: slug,
  };
};

記事データの取得処理(listArticlesByDate

load関数内で呼び出されているlistArticlesByDateの実装も見てみます。
web/src/lib/articles.tsにて、DynamoDB の GSI(ByDate)を使用して、指定された日付(date)に合致する記事をクエリしています。

// web/src/lib/articles.ts

export const listArticlesByDate = async (date: string): Promise<Article[]> => {
  const result = await docClient.send(
    new QueryCommand({
      TableName: ARTICLE_DATABASE_NAME,
      IndexName: "ByDate",
      KeyConditionExpression: "#dt = :dateValue",
      ExpressionAttributeNames: {
        "#dt": "date",
      },
      ExpressionAttributeValues: {
        ":dateValue": date,
      },
    })
  );

  return (result.Items ?? []) as Article[];
};

まとめ

SSG における動的パスの扱いは難しいですが、何らかの方法を使って「取りうる値の範囲」を定義することが重要です。今回は S3 パスから導出していますが、DB に持つなどするのもありだと思います。(というか ISG や ISR が最適な気もしなくもない・・・)

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?