Astro Container APIを使ってRSSで全文を配信する
Astro 4.9で実験的に Container API というものが追加された。これはざっくり説明すると今まで提供されていなかったAstroコンポーネントをHTML文字列に変換するAPIだ。当初はテスト用に開発されたが、いろんな場面で便利ということでユースケースが拡張されている。その中にPHPとの連携とかいう意味のわからないものもあるが、わかりやすいものとしてRSSで記事本文を配信するのに使える。ドキュメント で説明されている本文のレンダー方法は markdown-it
を使って記事ソースを変換する方法で、そのままではMDXには使えない。
以下のようにやってできた。
import rss from "@astrojs/rss";
import { type APIContext } from "astro";
import { getCollection } from "astro:content";
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { loadRenderers } from "astro:container";
import { getContainerRenderer } from "@astrojs/mdx";
// YYYY-MM-DD-title形式のslugから日付を抜き出す
function pubDate(slug: string) {
const [yyyy, mm, dd] = slug.split("-");
return new Date([yyyy, mm, dd].join("-"));
}
export async function GET(context: APIContext) {
if (!context.site) {
throw new TypeError("context.site falsy");
}
const container = await AstroContainer.create({
renderers: await loadRenderers([getContainerRenderer()]),
});
const blog = await getCollection("blog");
// yyyymmddをslugに入れてるせいかソートされてるのでreverseするだけでいいっぽい?
blog.reverse();
return rss({
title: "fuku.day/blog",
description: "AumyF's blog",
site: context.site,
items: await Promise.all(
blog.map(async (post) => ({
title: post.data.title,
description: post.data.description,
link: `/blog/${post.slug}`,
pubDate: pubDate(post.slug),
content: await container.renderToString((await post.render()).Content),
}))
),
});
}
なお、astro.config.js
で site
を設定しておく必要がある。
export default defineConfig({
site: "https://fuku.day",
// あれやこれや
});
astro/container
はContainer API本体で、astro:container
は4.101で追加されたContainer APIのヘルパーユーティリティが入ったバーチャルモジュールだ(このへんの使い分けがどのようになされているのかは知らない)。レンダーしたいコンテンツ中でReactやVueなどを使っているなら @astrojs/react
などそれぞれの getContainerRenderer
も使うことになる。
おわりに
Container APIでRSS全文配信をするという考えは_yuheiyさんのツイートのものです。
Footnotes
-
モジュール設定のミスがあって正確には4.10.3まで上げないと動かない ↩