【Astro】クエリパラメーターが処理されない問題を解決できた備忘録

Astroのblog templateを使用してホームページ+ブログを作っていたところ、記事をフィルターするためにクエリパラメーターを使用することにしました。

https://github.com/withastro/templates/tree/main/templates/blog

しかし、初歩的なミスにより（自分のssgへの理解度が低かったこともあるが）クエリパラメーターが処理されない事態が発生しましたが、解決できたので備忘録を残します。

# 状況
以下のようなブログの記事を縦に並べて一覧表示するページを実装しました。
`http://localhost:4321/blog`でアクセスできます。
```tsx:/src/pages/blog/index.astro
---
import { getCollection } from 'astro:content';
import Layout from '@/layouts/Layout.astro';
import PostHeadingTile from "@/components/PostHeadingTile";

const tag = Astro.url.searchParams.get("tag"); // クエリパラメーターを取得
console.log("tag :" + tag)

const posts = (await getCollection('blog')) // 記事を取得、タグでフィルターし、公開日付順に並べる
	.filter((post) => tag ? post.data.tag?.includes(tag) : true)
	.sort((a, b) => b.data.publishedDate.valueOf() - a.data.publishedDate.valueOf());
---
<Layout title='投稿一覧' description='ブログの投稿一覧'>
    <div class="p-4">
        <p class="text-xl font-bold">{posts.length}個の記事が見つかりました。</p>
        <section class="mt-4">
            <ul class="space-y-4">
                {
                    posts.map((post) => (
                        <li>
                            <PostHeadingTile category="blog" post={post}/>
                        </li>
                    ))
                }
            </ul>
        </section>
    </div>
</Layout>
```
クエリパラメーターを使用して、ブログの記事を`?tag=`でフィルターできるようにしました。Astroでクエリパラメーターを取得する方法は下の記事参考。

https://stackoverflow.com/questions/70173939/how-to-get-the-querystring-parameters-with-astro

これを実行し、`http://localhost:4321/blog?tag=Game`にアクセスしましたが、`/blog`にアクセスしたときと変わりませんでした。
また、console.log()の結果、`tag :null`と出力され、クエリパラメーターが正しく処理されていないことがわかりました。

# 原因
> AstroはデフォルトでSSGである

勘のいい方はこれ↑を見て解決法もわかったと思います。
自分は設定を特にいじらずにレンダリングモードはSSGのままにしていたので、ページは全て事前レンダリングになっていました。
ということは、クエリパラメーターを用いたページはそもそも生成されていないため、アクセスしても無視されるわけですね。

しかも、この記事書いてる途中に気づいたのですが、AstroのAPI Referenceにでかでかと「デフォルト設定のSSGだとこうなるよ！」っていうのが書いてありました。

>With the default output: 'static' option, Astro.request.url does not contain search parameters, like ?foo=bar, as it’s not possible to determine them ahead of time during static builds. However in output: 'server' mode, Astro.request.url does contain search parameters as it can be determined from a server request.

https://docs.astro.build/ja/reference/api-reference/#astrorequest

普通に見落としてたのやばすぎ。

# 解決法
まずは、設定をSSGからSSRに変えます。でもAstroの強みはSSGなので、これを捨てたくありません...
なのでレンダリングモードをSSGとSSRのHybridにします。

astroの設定ファイルに次のコードを追加します。
```TypeScript:astro.config.mjs
export default defineConfig({
  output: "hybrid", // <-これを追加して、レンダリングモードをstaticからhybridにする。
});
```

そして、SSRを適用したいページに`export const prerender = false`を追記すればOK。
```tsx:/src/pages/blog/index.astro
---
import { getCollection } from 'astro:content';
import Layout from '@/layouts/Layout.astro';
import PostHeadingTile from "@/components/PostHeadingTile";

const tag = Astro.url.searchParams.get("tag"); // クエリパラメーターを取得
console.log("tag :" + tag)

const posts = (await getCollection('blog')) // 記事を取得、タグでフィルターし、公開日付順に並べる
	.filter((post) => tag ? post.data.tag?.includes(tag) : true)
	.sort((a, b) => b.data.publishedDate.valueOf() - a.data.publishedDate.valueOf());

 export const prerender = false; // これを追加
---
<Layout title='投稿一覧' description='ブログの投稿一覧'>
    <div class="p-4">
        <p class="text-xl font-bold">{posts.length}個の記事が見つかりました。</p>
        <section class="mt-4">
            <ul class="space-y-4">
                {
                    posts.map((post) => (
                        <li>
                            <PostHeadingTile category="blog" post={post}/>
                        </li>
                    ))
                }
            </ul>
        </section>
    </div>
</Layout>
```
こうすることで、部分的なSSRが適用できます。

# 最後に
ちゃんとSSGの特性を理解して、原因と解決法を閃くことができる頭さえあればすぐ解決できる問題でした。
こんなことにも気づけない自分の頭に絶望していますが、これからもなんとか頑張っていきたいです。