【第6回】Learn Next.js チュートリアル解説:PostgreSQL データベースの構築と環境構築(Vercel連携)

Learn Next.jsでPostgreSQLデータベースを構築し、環境変数を設定してVercelと連携する仕組みの図解 プログラミング

前回(第5回)は、next/link や usePathname() を用いた高速で快適なページ間ナビゲーション(プリフェッチやアクティブ表示)について学びました。

第6回となる今回は、ダッシュボードアプリで本物のデータを扱うために不可欠な「PostgreSQL データベースのセットアップ」と「Vercel へのデプロイ・環境変数の設定」を行っていきます!

💡 今回学ぶこと(トピック)

  1. GitHub リポジトリへのプッシュと Vercel への初回デプロイ
  2. Vercel 上での PostgreSQL(Postgres)データベース作成と接続設定
  3. シード(Seeding)スクリプトを実行して初期データを投入する
  4. SQL クエリを実行してデータベースとの接続確認を行う

1. GitHub へのプッシュと Vercel デプロイ

データベースの設定をスムーズに進めるため、まずはプロジェクトを GitHub にプッシュし、Vercel と連携させます。

Step 1: GitHub にリポジトリを作成してプッシュ

作成したローカルのコードを GitHub のリモートリポジトリにプッシュします(GitLab や Bitbucket でも可)。

前提準備:GitHub 上で空のリポジトリを作成する

  1. GitHub にログインし、右上の 「+」アイコン ➔「New repository」 をクリックします。
  2. Repository name(例: nextjs-dashboard)を入力します。
  3. 「Public」(または Private)を選択します。
  4. ⚠️ 注意点:Add a README file や .gitignore などのチェックボックスは すべて外した状態(完全に空のリポジトリ) で「Create repository」ボタンを押します。
  5. 作成後に表示されるリポジトリの URL をコピーしておきます。

ローカル端末で実行するコマンド一覧

ターミナルを開き、プロジェクトのルートディレクトリで以下のコマンドを順番に実行します。

# 1. 変更されたすべてのファイルをステージング(追加)
git add .

# 2. ローカルリポジトリにコミットを作成
git commit -m "Initial commit: Set up Next.js dashboard project"

# 3. デフォルトブランチ名を main に変更(すでに main の場合はそのままでOK)
git branch -M main

# 4. GitHub のリモートリポジトリを登録(※URLはご自身のリポジトリURLに置き換えてください)
git remote add origin https://github.com/hisao5232/nextjs-dashboard.git

# 5. GitHub(リモートの main ブランチ)へコードを送信(プッシュ)
git push -u origin main

Step 2: Vercel アカウントの作成と連携

  1. vercel.com/signup にアクセスし、無料の「Hobby」プランを選択します。
  2. 「Continue with GitHub」を選び、GitHub アカウントと連携します。

Step 3: プロジェクトのインポートとデプロイ

Vercel のダッシュボードから先ほど作成した GitHub リポジトリを選択し、「Deploy」をクリックします。

vercelのダッシュボードからgithubリポジトリをインポートするページのスクリーンショット

🎉 これだけで最初のデプロイが完了します!

そして以下のプロジェクトのデプロイ管理画面の右上にある Visit ボタンから、デプロイされたWEBアプリにアクセスすることができます。

vercelのデプロイ管理画面のスクリーンショット

💡 Vercel 連携の強み:

GitHub の main ブランチにコードを Git プッシュするだけで、Vercel が自動で再デプロイを行ってくれます。また、プルリクエスト(PR)を作成した際も、開発メンバーと事前に確認できる「プレビューURL」が自動生成されます。

2. Postgres データベースの作成と環境変数(.env)の設定

次に、ダッシュボードで使うデータベースを作成します。

Step 1: Vercel で Postgres (Neon) を作成

  • Vercel のプロジェクトダッシュボードから 「Storage」 タブを開き、「Create Database」 を選択します。
vercelからデータベースを作成する画面のスクリーンショット
  • 複数の選択肢がありますが、ここでは公式の Learn Next.js のと同じようにプロバイダは Neon を選択します。

Neon(ネオン)とは?

NeonのサーバーレスPostgreSQLにおけるデータベースブランチと自動スリープの仕組みを示す図解

Next.js のエコシステムにおいて、Vercel Postgres の標準バックエンドとして一躍有名になった Neon(ネオン)ですが、データベースの「ブランチ作成(Branching)」、自動スリープと Instant Start(ゼロへのスケール)、自動スケーリング(Auto-Scaling)などの特徴があります。

Neon(ネオン)については別の記事で説明する予定ですのでそちらを参考にしてください。

リージョン選択

日本国内のユーザーを対象にする場合、選択肢の中では「シンガポール(ap-southeast-1)」を選ぶのがベストです。

データベースへのリクエストは1回の画面表示で複数回走ることが多いため、レイテンシーがダイレクトにページの表示速度やレスポンスの体感速度に影響します。日本に最も近いシンガポールが最もレスポンスが速くなります。

Neon(ネオン)の公式料金プラン(Pricing Plans)の設定

次に Neon の料金プランを選択します。個人開発や学習、今回のチュートリアルには Free を選択します(クレジットカードの登録も不要)。

  • Storage (1 GB per project):1プロジェクトあたり 1 GB までデータを保存可能(テキストデータなら膨大な量が入ります)。
  • Maximum projects (100):アカウント内で最大 100 個 までプロジェクト(DB)を作成可能。
  • Sizes up to (2 CU, 8 GB RAM):負荷がかかった際、最大で 2 CU(Compute Unit:2コア相当 / 8GBメモリ) まで自動でスケーリング(性能増強)してくれます。
  • Compute time (100 CU-hours per project):1ヶ月あたり 100 CU時間分 の計算リソースが使えます。Neon はアクセスがないと自動でスリープ(計算時間の消費ゼロ)するため、個人開発のアクセス量であればまず上限に達することはありません。
その他の有料プラン(Launch / Scale)
  • Launch: 本番サービスとしてアプリを立ち上げる(Launch)際のリソースやサポートが揃った有料プラン。
  • Scale: 大規模なアクセスや、高度なセキュリティ・コンプライアンスが必要な企業向けの最上位プラン。

Neon データベースの最終確認(Confirmation)

Resource Name

  • Vercel や Neon のダッシュボード上で表示されるデータベースの識別名(プロジェクト名)です。自分で任意の名前を入力して設定できます。

入力出来たら画面下部にある 「Create」 ボタンをクリックしてください。

Vercel と Neonの連携・接続設定 (Install Integration)

作成した Neon データベースを、Vercel 上のどの Next.js プロジェクトに紐付け、どのような環境変数として登録するかを設定するステップになります。

1. Connect a Project(プロジェクトの接続)
  • Project: データベースを紐付けたい Vercel 上のプロジェクトを選択するドロップダウンです。先ほど GitHub から Vercel にインポートした リポジトリ を選択します。
2. Environments(環境の選択)
  • Create database branch for deployment:
    「Production(本番)」 と 「Preview(テスト環境)」 の両方にチェックを入れたままで OK です。Vercel と Neon の強みである「Git ブランチごとの DB 自動連携」が有効になります。
3. Custom Prefix(環境変数の接頭辞)
  • _URL: Vercel に自動生成される環境変数の名前につくプレフィックスです。空欄や POSTGRES 等で問題ありません。

最後に画面下の方にある「Connect」 ボタンをクリックします。

これで Vercel のプロジェクトと Neon データベースが自動で接続されます。

Step 2: 接続情報(.env に貼り付ける文字列)を取得

以下のような接続情報(.env に貼り付ける文字列)を取得する画面へ進みます。

Neonデータベースの管理画面のスクリーンショット

Step 3: 環境変数(.env)の取得とローカルへの設定

  1. 接続が完了したら 「.env.local」 タブを開き、右側にある「Show secret」をクリックしてからコード(環境変数)をコピーします。
  2. ローカル環境(VS Code 等)を開き、プロジェクト直下にある .env.example ファイルを .env にリネームします。
  3. コピーした接続情報を .env ファイル内に貼り付けます。
# .env のイメージ(Vercelから取得したデータベース接続情報)
POSTGRES_URL="..."
POSTGRES_PRISMA_URL="..."
POSTGRES_URL_NON_POOLING="..."
POSTGRES_USER="..."
POSTGRES_HOST="..."
POSTGRES_PASSWORD="..."
POSTGRES_DATABASE="..."

⚠️ 重要セキュリティチェック:

データベースのパスワードや接続文字列が GitHub 等に公開されないよう、.gitignore ファイル内に .env が含まれていることを必ず確認してください。

✍️ 「Docker もローカルDB構築も要らない」Cloud Native 時代の DB セットアップの衝撃

これまで Web アプリで PostgreSQL を使おうとすると、ローカル環境に PostgreSQL を直インストールするか、docker-compose.yml を書いて Docker コンテナを立ち上げ、接続ポートやユーザー権限を設定する……という「本題に入る前の準備」で時間をかけていました。

しかし、Vercel の Storage を使った開発では、画面上で数クリックするだけで本番用・開発用のクラウド PostgreSQL が一瞬で立ち上がり、生成された秘密鍵を .env に貼るだけでローカル開発環境から直接クラウド DB へ接続が完了してしまいます。

VPSの容量を気にすることもなく、インフラ構築の煩わしさを極限まで削ぎ落とし、「コードを書くこと(開発)に集中させてくれる」現代のフルスタック開発体験に衝撃を感じました。

3. シード(Seeding)による初期データの投入

データベースが準備できたら、初期データを投入(シード)します。

これから読み込むのは app/lib/placeholder-data.ts の中にチュートリアルの初期データとして最初から仕込まれている Learn Next.js 公式のダミーデータです。

❓ データベースの「シード(Seeding)」とは?

データベースを立ち上げた直後の空の状態に対して、開発やテストで使うための初期データ(ユーザー情報、請求書データなど)を自動で投入・流し込む作業のことです。

シードスクリプトの実行手順

  1. ローカル開発サーバーが起動していることを確認します(起動していなければ npm run dev)。
  2. ブラウザで http://localhost:3000/seed にアクセスします。
  3. 裏側で SQL(/app/seed/route.ts から app/lib/placeholder-data.ts のデータ挿入)が実行され、画面に “Database seeded successfully” と表示されれば完了です!
Next.jsのRoute Handler(route.ts)を使い、URLへのアクセスをきっかけにNeonのPostgreSQLデータベースへシードデータを自動挿入する仕組みの図解

※シード完了後は、この /app/seed/route.ts は不要になるため削除して問題ありません。

なぜ「完了したら /app/seed を削除してよい」のか?

もしこの /app/seed というルートを残したまま本番環境(Vercel 等)に公開してしまうと、「誰かが https://あなたのアプリ.com/seed にアクセスして、本番のデータベースを勝手に初期化・上書きできてしまう」 という致命的な脆弱性(危険性)になってしまいます。

そのため、開発環境で一度ブラウザからアクセスして DB にデータが入ったことを確認したら、本番環境へ進む前にこの /app/seed フォルダ(または route.ts)は丸ごと削除してしまって OK なのです。

💡 トラブルシューティング:PostgresError (code: 23505) が出た場合

ブラウザで http://localhost:3000/seed にアクセスした際、もし以下のような JSON エラーが表示された場合の対処法です。

{
  "error": {
    "name": "PostgresError",
    "code": "23505",
    "detail": "Key (extname)=(uuid-ossp) already exists.",
    ...
  }
}

原因は CREATE EXTENSION IF NOT EXISTS “uuid-ossp” の実行タイミング(競合) にあります。

シード処理内で Promise.all を使って複数のテーブル(users, invoices, customers)を並列処理する際、それぞれの関数内で同時に「UUID 拡張機能を作成せよ」という命令が PostgreSQL に飛んでしまいます。

IF NOT EXISTS(存在しなければ作成)と書いてあっても、ミリ秒単位で完全に同時に命令が届くと、データベース側で重複検知(レースコンディション)が起きてエラーになってしまうのです。

🛠️ 解決策:拡張機能の作成を「一番最初」に1回だけ行う

各テーブル作成関数(seedUsers や seedInvoices など)の中にある CREATE EXTENSION の行を削除し、親の GET() 関数の冒頭で 1回だけ単独実行 するよう修正します。

export async function GET() {
  try {
    // ★ 1. トランザクションに入る前に、拡張機能を1回だけ有効化しておく
    await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;

    // ★ 2. その後に各テーブルの作成とデータ投入を安全に実行する
    const result = await sql.begin((sql) => [
      seedUsers(),
      seedCustomers(),
      seedInvoices(),
      seedRevenue(),
    ]);

    return Response.json({ message: 'Database seeded successfully' });
  } catch (error) {
    return Response.json({ error }, { status: 500 });
  }
}

コードを保存して再度 http://localhost:3000/seed にアクセスし、{“message”:”Database seeded successfully”} と表示されれば無事シード完了です!

4. SQL クエリの実行確認

データベースと正しく通信できるか確かめるため、実際に SQL を発行してデータを取得してみましょう。

/app/query/route.ts のコメントアウトを外して、以下のような コードに変更します。

import postgres from 'postgres';

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

async function listInvoices() {
  const data = await sql`
    SELECT invoices.amount, customers.name
    FROM invoices
    JOIN customers ON invoices.customer_id = customers.id
    WHERE invoices.amount = 666;
  `;

  return data;
}

export async function GET() {
  try {
    return Response.json(await listInvoices());
  } catch (error) {
    return Response.json({ error }, { status: 500 });
  }
}
  1. コード変更によって、Response.json() を返す処理を有効にします。
  2. ブラウザで http://localhost:3000/query にアクセスします。
  3. 請求額(666)と顧客名が合致した JSON データが画面に返ってくれば、データベースのセットアップと接続テストは完全に成功です!

❓ クイズで復習!

Q1: データベースのコンテキストにおける「シード(Seeding)」とは何を意味しますか?

  • A: データベース内のすべてのデータを削除すること
  • B: データベースのスキーマ(構造)をインポートすること
  • C: データベースに初期データ群を投入・補充すること
  • D: データベースのテーブル間にリレーションを作成すること

正解: C(Populating the database with an initial set of data)

開発やテストを始めるために、あらかじめ用意された初期サンプルデータをデータベースへ注入するプロセスを「シード」と呼びます。

Q2: 先ほどの SQL クエリ(WHERE invoices.amount = 666;)を実行した際、この請求書(amount: 666)はどの顧客(Customer)のものとして取得されましたか?

  • A: Lee Robinson
  • B: Evil Rabbit
  • C: Delba de Oliveira
  • D: Michael Novotny

正解: B(Evil Rabbit)

JOIN 句によって invoices テーブルと customers テーブルが結合され、金額が 666 の請求書に紐づく顧客名として「Evil Rabbit」が取得されます。

まとめ

第6回では、Next.js アプリケーションのバックエンドを支えるデータベース環境を構築しました。

  • Vercel × GitHub:プッシュするだけの自動デプロイ環境の確立。
  • Vercel Postgres:クラウド上の PostgreSQL データベースの作成と .env による安全な接続設定。
  • Seeding と SQL:初期データの投入と、SQL クエリを用いた接続・疎通確認。

次回はいよいよ「第7章:データの取得(Fetching Data)」に入ります!今回作成したデータベースから、Server Components や SQL を使って効率的にデータを取得・表示する方法を学んでいきましょう!

コメント