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?

supabase接続でCONNECTION_CLOSEDになる

Posted at

Next.js + Vercel + Supabaseでデータベースシード時の接続エラー完全解決ガイド

1. はじめに

📚 この記事の経緯

Next.jsの公式チュートリアル「Learn Next.js」の第6章「Setting Up Your Database」を進めている時に発生した接続エラーと、その解決過程を記録した記事です。

🎯 対象読者

  • Next.jsのチュートリアルを進めている初心者
  • Supabaseでデータベース接続エラーに遭遇した方
  • CONNECTION_CLOSEDエラーで困っている方

💡 記事の価値

  • 教科書通りに進めてもエラーが起きる実例
  • 具体的なエラーメッセージと解決手順
  • Supabaseの接続方式の理解

2. 環境構成

使用技術

  • Next.js: 15.3.2 (Turbopack)
  • ホスティング: Vercel
  • データベース: Supabase (PostgreSQL)
  • パッケージマネージャー: pnpm

プロジェクト構成

nextjs-dashboard/
├── app/
│   ├── seed/
│   │   └── route.js          # シードスクリプト
│   └── lib/
│       └── placeholder-data.ts # サンプルデータ
├── .env                        # 環境変数
└── package.json

3. シードスクリプトとは

🌱 シード(seed)とは

データベースの「種まき」という意味で、初期データを投入する処理のことです。

Next.jsチュートリアルでの役割:

  • 空のデータベースにサンプルデータを投入
  • ユーザー、顧客、請求書、売上データを作成
  • アプリケーションがすぐに動作確認できる状態にする

実際の処理内容:

// app/seed/route.ts の主な処理
import bcrypt from 'bcrypt';
import postgres from 'postgres';
import { invoices, customers, revenue, users } from '../lib/placeholder-data';

const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });

async function seedUsers() {
  await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
  await sql`
    CREATE TABLE IF NOT EXISTS users (
      id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
      name VARCHAR(255) NOT NULL,
      email TEXT NOT NULL UNIQUE,
      password TEXT NOT NULL
    );
  `;

  const insertedUsers = await Promise.all(
    users.map(async (user) => {
      const hashedPassword = await bcrypt.hash(user.password, 10);
      return sql`
        INSERT INTO users (id, name, email, password)
        VALUES (${user.id}, ${user.name}, ${user.email}, ${hashedPassword})
        ON CONFLICT (id) DO NOTHING;
      `;
    }),
  );

  return insertedUsers;
}

なぜ時間がかかるのか:

  • 複数のテーブル作成(users, customers, invoices, revenue)
  • bcryptでのパスワードハッシュ化(セキュリティのため)
  • 大量のINSERT文の実行

4. 発生した問題

4-1. CONNECTION_CLOSEDエラー

症状

  • localhost:3000/seed にアクセス
  • 1分以上Pending状態が続く
  • 最終的にエラーが返される

エラーメッセージ

{"error":{"code":"CONNECTION_CLOSED","errno":"CONNECTION_CLOSED","address":["aws-0-us-east-1.pooler.supabase.com"],"port":[6543]}}

原因
Supabaseの接続プール(6543番ポート)による長時間処理の強制切断

5. 解決策: 接続方式の変更

5-1. Supabaseの接続方式の理解

🔌 Supabaseへの2つの接続方法

Supabaseには2つの入り口があります:

接続プール(共有入り口)
あなたのアプリ → 共有受付 → Supabaseデータベース
                (6543番)

仕組み:

  • 共有の受付窓口を通してデータベースにアクセス
  • 複数のアプリが同じ窓口を使い回し
  • 窓口が「効率化のために」接続を管理

メリット:

  • 速い(すでに準備された接続を使える)
  • 効率的(Supabase側の負荷が少ない)

デメリット:

  • 時間制限あり:長時間使うと「他の人が待ってるから」と切断される
  • 制限が厳しい:無料プランでは特に厳格
直接接続(専用入り口)
あなたのアプリ ──────→ Supabaseデータベース
                   (5432番)

仕組み:

  • 専用の入り口から直接データベースにアクセス
  • 他のアプリの影響を受けない
  • 時間制限が緩い

メリット:

  • 安定:途中で切断されにくい
  • 長時間処理対応:シードなどの重い処理も大丈夫

デメリット:

  • 少し遅い(毎回新しい接続を作成)
  • Supabase側の負荷が高い

🚨 なぜシード処理で問題になったか

シードスクリプトの処理:

  1. テーブルを作成(10秒)
  2. ユーザーのパスワードをハッシュ化(30秒)← 時間かかる
  3. データを挿入(20秒)

接続プール(6543番)の場合:

  • 受付窓口が「1分以上は長すぎる!他の人が待ってる!」
  • 強制的に接続を切断CONNECTION_CLOSEDエラー

直接接続(5432番)の場合:

  • 専用入り口なので時間制限が緩い
  • 最後まで処理完了 → 成功

5-2. 具体的な解決手順

# Step 1: 現在の設定削除
sed -i '' '/^POSTGRES_URL=/d' .env

# Step 2: NON_POOLING接続を使用
grep POSTGRES_URL_NON_POOLING .env | sed 's/POSTGRES_URL_NON_POOLING=/POSTGRES_URL=/' >> .env

# Step 3: 開発サーバー再起動
pnpm run dev

5-3. 成功結果

{"message":"Database seeded successfully"}

6. エラーの詳細解説

6-1. CONNECTION_CLOSED - Supabase接続タイムアウト

🔍 なぜこのエラーが起きるのか

Supabaseの接続プール(6543番ポート)には制限があります:

  • 時間制限: 長時間の処理は途中で切断される
  • 👥 共有制限: 他のユーザーと共有しているため優先度が低い
  • 💰 無料プラン制限: 特に厳しい制限がかかる

実際に起きたこと:

  1. シードスクリプトがSupabaseに接続(6543番ポート)✅
  2. テーブル作成開始 ✅
  3. bcryptでパスワードのハッシュ化(時間がかかる処理)⏳
  4. Supabase側が「この接続は長すぎる」と判断
  5. 強制的に接続を切断CONNECTION_CLOSEDエラー

🎯 解決方法

  • 6543番(接続プール)→ 5432番(直接接続)に変更
  • 直接接続なら時間制限が緩い

7. ベストプラクティス

7-1. 接続設定の選択指針

🎯 Supabaseでの接続方法の使い分け

処理タイプ 使用ポート 理由
通常のWebアプリ 6543番(接続プール) 速い、効率的
シード・データ移行 5432番(直接接続) 長時間処理対応

📊 Vercelが提供する環境変数の使い分け

# 通常のアプリケーション用(速い)
POSTGRES_URL="...pooler.supabase.com:6543..."

# シード・長時間処理用(安定)  
POSTGRES_URL_NON_POOLING="...pooler.supabase.com:5432..."

🔄 実際の切り替え方法

# シード時は NON_POOLING を使用
sed -i '' '/^POSTGRES_URL=/d' .env
grep POSTGRES_URL_NON_POOLING .env | sed 's/POSTGRES_URL_NON_POOLING=/POSTGRES_URL=/' >> .env

7-2. トラブルシューティングの順序

  1. 環境変数の確認(POSTGRES_URLの設定)
  2. 接続方式の変更(プール接続から直接接続へ)
  3. 段階的な変更でテスト

8. 参考リンク

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?