0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.js App Router × Supabaseの個人開発でハマった3つの罠(sitemap.xml 404 / PostgRESTあいまいembed / 外部リンクのpreconnect高速化)

0
Posted at

個人開発で美容・サプリのコスパ比較メディア(mirucos)を Next.js 14 App Router + Supabase + Vercel で作っています。その中で地味に時間を溶かした3つの罠と解決法をメモします。同じ構成の人の役に立てば。

1. App Routerで sitemap.xml が404になる(generateSitemaps の罠)

app/sitemap.tsgenerateSitemaps() を使うと、実際に配信されるのは /sitemap/0.xml などのシャード版で、/sitemap.xml 自体は 404 になります。robots.txt から /sitemap.xml を参照していると、Search Consoleで「サイトマップを取得できませんでした」になって地味にハマります。

URL数が5万未満なら単一サイトマップで十分なので、generateSitemaps を使わず素直に1ファイルにします。

ts// app/sitemap.ts
import type { MetadataRoute } from "next";

export const revalidate = 3600;

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const base = "https://example.com";
    const slugs = await getPublishedSlugs(); // DBから取得
      return [
          { url: `${base}/`, changeFrequency: "weekly", priority: 1 },
              ...slugs.map((s) => ({ url: `${base}/product/${s}`, priority: 0.7 })),
                ];
                }
                ```

                これで `/sitemap.xml` が `200 / application/xml` で返るようになります。

                ## 2. Supabase(PostgREST)の「あいまいembed」エラー(PGRST201)

                2つのテーブル間に**外部キーが2本**あると、ネストした `select` がどのFK経由か曖昧になり `PGRST201` で落ちます。

                例:`articles.product_id  products.id` と `products.article_id  articles.id` の相互参照があるケース。

                ```ts
                // ❌ あいまいでエラー(Could not embed because more than one relationship was found)
                client.from("articles").select("*, products(*)");

                // ✅ FK名を明示すれば解決
                client.from("articles").select("*, product:products!articles_product_id_fkey(*)");
                ```

                `!inner` を付ければINNER JOIN相当になり、`products` 側カラムでのフィルタも効きます。

                ```ts
                client
                  .from("articles")
                    .select("*, product:products!articles_product_id_fkey!inner(*)")
                      .eq("products.category_slug", "skincare");
                      ```

                      ## 3. 外部リンク(アフィリンク等)の遷移を `preconnect` で体感高速化

                      外部ドメインへの遷移は毎回 DNS解決 + TCP + TLS が走って待たされます。**ホバーした時点で宛先オリジンへ `preconnect` を打っておく**と、クリック時にはハンドシェイク済みで体感がかなり変わります。

                      ```tsx
                      // ホバー/フォーカスでpreconnectを動的注入(簡略版)
                      function warmOrigin(origin: string) {
                        for (const rel of ["preconnect", "dns-prefetch"]) {
                            const l = document.createElement("link");
                                l.rel = rel;
                                    l.href = origin;
                                        document.head.appendChild(l);
                                          }
                                          }
                                          ```

                                          - 内部リンク:`router.prefetch(path)` でRSCを先読み
                                          - - 外部リンク:宛先オリジンへ `preconnect` / `dns-prefetch`

と使い分けると、回遊も外部遷移も軽くなります。よく使う外部ドメインは `app/layout.tsx` の `<head>` に静的に `preconnect` を置いておくのも有効です。

## おまけ:TailwindでCSS変数カラー + 不透明度が効かない

`--brand` のようなCSS変数で定義した色に対して `bg-brand/20` のようなalpha修飾子を付けても**描画されない**ことがあります(変数の形式次第)。素の色トークン(`amber-50` 等)を使うか、`<alpha-value>` を仕込んだ定義にするのが無難でした。

---

以上、Next.js App Router + Supabase で個人開発する人の時短になれば幸いです。実際に動かしているサイトは [mirucos(コスメ・サプリのコスパ比較メディア)](https://mirucos.com) です。何か間違いや改善点があればコメントで教えてください🙏
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?