neon-vercel-postgres について
サーバーレスやエッジ環境にアプリをデプロイする開発者向けに、Neon または Vercel Postgres の利用方法を解説するスキルです。設定、安全なクエリ、接続プーリング、スキーマ管理、デプロイ時の注意点を扱います。
主な機能
- 01Neon と Vercel の設定ガイド
- 02サーバーレス接続プーリング
- 03SQL テンプレートタグによるクエリ
- 04Drizzle と Prisma のスキーマ運用
- 05トランザクションとデプロイの手順
活用シーン
- 01Cloudflare Workers から Postgres に接続
- 02Vercel で Next.js アプリを構築
- 03サーバーレス環境で接続枯渇を防止
- 04スキーマ作成とマイグレーション実行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill neon-vercel-postgresインストール後、Claude Code で/neon-vercel-postgresを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.md.claude-plugin/plugin.jsonassets/drizzle-schema.tsexample-template.txtreferences/example-reference.mdscripts/example-script.shtest-connection.tstemplates/drizzle-migrations-workflow.mddrizzle-queries.tsdrizzle-schema.tsneon-basic-queries.tspackage.json詳細
Neon & Vercel Serverless Postgres
ステータス:本番環境対応済み
最終更新日:2026-01-21
依存関係:なし
最新バージョン:@neondatabase/[email protected]、@vercel/[email protected]、[email protected]、[email protected]、[email protected]
クイックスタート(5 分)
1. プラットフォームを選択する
オプション A:Neon Direct(マルチクラウド、Cloudflare Workers、あらゆるサーバーレス環境)
npm install @neondatabase/serverless
オプション B:Vercel Postgres(Vercel 専用、Vercel で設定不要)
npm install @vercel/postgres
注:どちらも同じ Neon バックエンドを使用します。Vercel Postgres は、Vercel 固有の環境設定を備えた Neon です。
重要な理由:
- Neon Direct では、マルチクラウドの柔軟性とブランチ API を利用できます
- Vercel Postgres では、Vercel 上で設定不要で、自動的に環境変数が設定されます
- どちらも HTTP ベース(TCP 不使用)で、サーバーレス/エッジ環境に最適です
2. 接続文字列を取得する
Neon Direct の場合:
# Sign up at https://neon.tech
# Create a project → Get connection string
# Format: postgresql://user:[email protected]/dbname?sslmode=require
Vercel Postgres の場合:
# In your Vercel project
vercel postgres create
vercel env pull .env.local # Automatically creates POSTGRES_URL and other vars
重要:
- サーバーレスではプール済み接続文字列(末尾が
-pooler.region.aws.neon.techのもの)を使用してください - プールされていない接続は、サーバーレス環境ではすぐに枯渇します
- 必ず
?sslmode=requireパラメーターを含めてください
3. データベースにクエリを実行する
Neon Direct(Cloudflare Workers、Vercel Edge、Node.js):
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
// Simple query
const users = await sql`SELECT * FROM users WHERE id = ${userId}`;
// Transactions
const result = await sql.transaction([
sql`INSERT INTO users (name) VALUES (${name})`,
sql`SELECT * FROM users WHERE name = ${name}`
]);
Vercel Postgres(Next.js Server Actions、API Routes):
import { sql } from '@vercel/postgres';
// Simple query
const { rows } = await sql`SELECT * FROM users WHERE id = ${userId}`;
// Transactions
const client = await sql.connect();
try {
await client.sql`BEGIN`;
await client.sql`INSERT INTO users (name) VALUES (${name})`;
await client.sql`COMMIT`;
} finally {
client.release();
}
重要:
- SQL インジェクションを自動的に防ぐには、テンプレートタグ構文(
sql`...`)を使用してください - 文字列を連結しないでください:
sql('SELECT * FROM users WHERE id = ' + id)❌ - テンプレートタグは値を自動的にエスケープし、SQL インジェクションを防ぎます
7 ステップのセットアップ手順
ステップ 1:パッケージをインストールする
デプロイ先のプラットフォームに応じて選択してください:
Neon Direct(Cloudflare Workers、マルチクラウド、Neon への直接アクセス):
npm install @neondatabase/serverless
Vercel Postgres(Vercel 専用、設定不要):
npm install @vercel/postgres
ORM を使用する場合:
# Drizzle ORM (recommended for edge compatibility)
npm install [email protected] @neondatabase/[email protected]
npm install -D [email protected]
# Prisma (Node.js only)
npm install prisma @prisma/client @prisma/adapter-neon @neondatabase/serverless
ポイント:
- どちらのパッケージも HTTP/WebSocket を使用します(TCP は不要)
- エッジに対応しています(Cloudflare Workers、Vercel Edge Runtime で動作します)
- プール済み接続文字列を使用すると、接続プーリングが組み込まれます
- 別途、接続プールライブラリを用意する必要はありません
ステップ 2:Neon データベースを作成する
オプション A:Neon ダッシュボード
- https://neon.tech でサインアップします
- 新しいプロジェクトを作成します
- プール済み接続文字列をコピーします(重要です!)
- 形式:
postgresql://user:[email protected]/db?sslmode=require
オプション B:Vercel ダッシュボード
- Vercel プロジェクト → Storage → Create Database → Postgres に移動します
- Vercel が自動的に Neon データベースを作成します
vercel env pullを実行して、ローカルで環境変数を取得します
オプション C:Neon CLI([email protected])
# Install CLI
npm install -g [email protected]
# Authenticate
neonctl auth
# Create project and get connection string
neonctl projects create --name my-app
neonctl connection-string main
重要:
- 必ずプール済み接続文字列(末尾が
-pooler.region.aws.neon.techのもの)を使用してください - プールされていない接続は直接接続用です(サーバーレスでは使用しません)
- 接続文字列に
?sslmode=requireを含めてください
ステップ 3:環境変数を設定する
Neon Direct の場合:
# .env or .env.local
DATABASE_URL="postgresql://user:[email protected]/neondb?sslmode=require"
Vercel Postgres の場合:
# Automatically created by `vercel env pull`
POSTGRES_URL="..." # Pooled connection (use this for queries)
POSTGRES_PRISMA_URL="..." # For Prisma migrations
POSTGRES_URL_NON_POOLING="..." # Direct connection (avoid in serverless)
POSTGRES_USER="..."
POSTGRES_HOST="..."
POSTGRES_PASSWORD="..."
POSTGRES_DATABASE="..."
Cloudflare Workers の場合(wrangler.jsonc):
{
"vars": {
"DATABASE_URL": "postgresql://user:[email protected]/neondb?sslmode=require"
}
}
ポイント:
- クエリには
POSTGRES_URL(プール済み)を使用してください - Prisma のマイグレーションには
POSTGRES_PRISMA_URLを使用してください - サーバーレス関数では
POSTGRES_URL_NON_POOLINGを使用しないでください - シークレットは安全に保管してください(Vercel env、Cloudflare secrets など)
ステップ 4:データベーススキーマを作成する
オプション A:生の SQL
// scripts/migrate.ts
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
await sql`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
)
`;
オプション B:Drizzle ORM(推奨)
// db/schema.ts
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow()
});
// db/index.ts
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
# Run migrations
npx drizzle-kit generate
npx drizzle-kit migrate
オプション C:Prisma
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("POSTGRES_PRISMA_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
createdAt DateTime @default(now()) @map("created_at")
@@map("users")
}
npx prisma migrate dev --name init
重要:
- エッジ対応の ORM には Drizzle を使用してください(Cloudflare Workers で動作します)
- Prisma には Node.js ランタイムが必要です(Cloudflare Workers では動作しません)
- マイグレーションはエッジ関数ではなく、Node.js 環境から実行してください
ステップ 5:クエリパターン
重要 — テンプレートタグ構文が必須です:
// ✅ Correct: Template tag syntax (prevents SQL injection)
const users = await sql`SELECT * FROM users WHERE email = ${email}`;
// ❌ Wrong: String concatenation (SQL injection risk)
const users = await sql('SELECT * FROM users WHERE email = ' + email);
Neon トランザクション API(独自の機能):
// Automatic transaction (array of queries)
const results = await sql.transaction([
sql`INSERT INTO users (name) VALUES (${name})`,
sql`UPDATE accounts SET balance = balance - ${amount} WHERE id = ${accountId}`
]);
// Manual transaction with callback (for complex logic)
const result = await sql.transaction(async (sql) => {
const [user] = await sql`INSERT INTO users (name) VALUES (${name}) RETURNING id`;
await sql`INSERT INTO profiles (user_id) VALUES (${user.id})`;
return user;
});
Vercel Postgres のトランザクション:
sql.connect()と手動のBEGIN/COMMIT/ROLLBACKを使用する必要があります- 接続リークを防ぐため、必ず
finallyブロックでclient.release()を呼び出してください
Drizzle のトランザクション:
await db.transaction(async (tx) => {
await tx.insert(users).values({ name, email });
await tx.insert(profiles).values({ userId: user.id });
});
ステップ 6:接続プーリングの処理
接続文字列の形式:
Pooled (serverless): postgresql://user:[email protected]/db
Non-pooled (direct): postgresql://user:[email protected]/db
使い分け:
- プール済み(
-pooler.):サーバーレス関数、エッジ関数、高い同時実行性が求められる場合 - プールなし:長時間稼働するサーバー、マイグレーション、管理タスク、接続数の制限が問題にならない場合
自動プーリング(Neon/Vercel):
// Both packages handle pooling automatically when using pooled connection string
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!); // Pooling is automatic
接続数の制限:
- Neon Free Tier:同時接続数は 100
- プール済み接続:リクエスト間で接続を共有します
- プールなし:リクエストごとに新しい接続を取得します(すぐに上限に達します)
重要:
- サーバーレス環境では、必ずプール済みの接続文字列を使用してください
- プールなしの接続では、「connection pool exhausted」エラーが発生します
- Neon ダッシュボードで接続の使用状況を監視してください
ステップ 7:デプロイとテスト
Cloudflare Workers:
// src/index.ts
import { neon } from '@neondatabase/serverless';
export default {
async fetch(request: Request, env: Env) {
const sql = neon(env.DATABASE_URL);
const users = await sql`SELECT * FROM users`;
return Response.json(users);
}
};
# Deploy
npx wrangler deploy
Vercel(Next.js API Route):
// app/api/users/route.ts
import { sql } from '@vercel/postgres';
export async function GET() {
const { rows } = await sql`SELECT * FROM users`;
return Response.json(rows);
}
# Deploy
vercel deploy --prod
テストクエリ:
# Local test
curl http://localhost:8787/api/users
# Production test
curl https://your-app.workers.dev/api/users
重要なポイント:
- デプロイ前にローカルでテストしてください
- Neon ダッシュボードでクエリのパフォーマンスを監視してください
- 接続プールの上限に達した場合のアラートを設定してください
- デバッグには Neon のクエリ履歴を使用してください
重要なルール(Neon/Vercel 固有)
✅ 必ず行うこと:
- サーバーレスでは、プール済み接続文字列(ホスト名に
-pooler.を含む)を使用する - 接続文字列に
?sslmode=requireを含める - SQL インジェクションを防ぐため、テンプレートタグ構文(
sql`...`)を使用する client.release()をfinallyブロックで呼び出す(Vercel Postgres のトランザクションのみ)- Cloudflare Workers では Drizzle を使用する(Prisma には Node.js ランタイムが必要です)
- クエリには
POSTGRES_URL、Prisma のマイグレーションにはPOSTGRES_PRISMA_URLを使用する
❌ 絶対に行わないこと:
- サーバーレスでプールなしの接続や
POSTGRES_URL_NON_POOLINGを使用する - SQL 文字列を連結する(テンプレートタグのみを使用する)
sslmode=requireを省略する(接続に失敗します)- Cloudflare Workers で Prisma を使用する(V8 アイソレートは Prisma をサポートしていません)
- エッジ関数からマイグレーションを実行する(Node.js 環境を使用してください)
既知の問題の防止
このスキルは、文書化されている 19 件の問題を防ぎます。
問題 #1:接続プールの上限超過
エラー:Error: connection pool exhausted または too many connections for role
出典:https://github.com/neondatabase/serverless/issues/12
発生原因:同時実行性の高いサーバーレス環境で、プールなしの接続文字列を使用している
防止策:必ずプール済み接続文字列を使用してください(ホスト名に -pooler. を含むもの)。接続文字列の形式を確認してください。
問題 #2:TCP 接続がサポートされていない
エラー:Error: TCP connections are not supported in this environment
出典:Cloudflare Workers のドキュメント
発生原因:従来の Postgres クライアントは TCP ソケットを使用しますが、エッジランタイムでは利用できない
防止策:pg や postgres.js パッケージの代わりに、@neondatabase/serverless(HTTP/WebSocket ベース)を使用してください。
問題 #3:文字列連結による SQL インジェクション
エラー:SQL インジェクション攻撃が成功する、または予期しないクエリ結果が返される
出典:OWASP SQL Injection Guide
発生原因:ユーザー入力を SQL 文字列に連結している:sql('SELECT * FROM users WHERE id = ' + id)
防止策:必ずテンプレートタグ構文を使用してください:sql`SELECT * FROM users WHERE id = ${id}`。テンプレートタグは値を自動的にエスケープします。
問題 #4:SSL モードの指定漏れ
エラー:Error: connection requires SSL または FATAL: no pg_hba.conf entry
出典:https://neon.tech/docs/connect/connect-securely
発生原因:接続文字列に ?sslmode=require パラメーターがない
防止策:必ず接続文字列の末尾に ?sslmode=require を追加してください。
Issue #5:接続リーク(Vercel Postgres)
エラー:メモリ使用量が徐々に増加し、最終的にタイムアウトエラーが発生します
原因: https://github.com/vercel/storage/issues/45
発生理由:手動トランザクション後に client.release() を呼び出し忘れています
防止策:常に try/finally ブロックを使用し、finally ブロックで client.release() を呼び出してください。
Issue #6:環境変数の誤り(Vercel)
エラー:Error: Connection string is undefined または connect ECONNREFUSED
原因: https://vercel.com/docs/storage/vercel-postgres/using-an-orm
発生理由:POSTGRES_URL の代わりに DATABASE_URL を使用している、またはその逆です
防止策:クエリには POSTGRES_URL を、Prisma のマイグレーションには POSTGRES_PRISMA_URL を使用してください。
Issue #7:エッジ関数でのトランザクションタイムアウト
エラー:Error: Query timeout または Error: transaction timeout
原因: https://neon.tech/docs/introduction/limits
発生理由:実行時間の長いトランザクションがエッジ関数のタイムアウト(通常 30 秒)を超過します
防止策:トランザクションを短く保ち(<5s)、操作をバッチ処理するか、複雑なトランザクションをバックグラウンドワーカーに移してください。
Issue #8:Cloudflare Workers での Prisma
エラー:Error: PrismaClient is unable to be run in the browser またはモジュール解決エラー
原因: https://github.com/prisma/prisma/issues/18765
発生理由:Prisma にはファイルシステムにアクセスできる Node.js ランタイムが必要です
防止策:Cloudflare Workers では Drizzle ORM を使用してください。Prisma は Vercel Edge/Node.js ランタイムでのみ動作します。
Issue #9:ブランチ API の認証エラー
エラー:Neon API の呼び出し時に Error: Unauthorized
原因: https://neon.tech/docs/api/authentication
発生理由:NEON_API_KEY 環境変数がないか、無効です
防止策:Neon ダッシュボードの「Account Settings」→「API Keys」で API キーを作成し、環境変数として設定してください。
Issue #10:ブランチ削除後の古い接続
エラー:ブランチ削除後に Error: database "xyz" does not exist
原因: https://neon.tech/docs/guides/branching
発生理由:アプリケーションが削除済みブランチの接続文字列を引き続き使用しています
防止策:ブランチを切り替える際に DATABASE_URL を更新し、ブランチの変更後にアプリケーションを再起動してください。
Issue #11:コールドスタート/自動一時停止時のクエリタイムアウト
エラー:アイドル期間後の最初のリクエストで Error: Query timeout、または Connection terminated unexpectedly
原因: Neon ドキュメント - 自動一時停止 | GitHub Issue #168 | 2025 年 12 月の変更履歴
発生理由:Neon は非アクティブ状態が約 5 分続くとコンピュートを自動一時停止します(無料プラン)。これにより、約 1~2 秒の起動遅延や接続の切断が発生します
防止策:
- コールドスタートを考慮し、クエリタイムアウトを 10 秒以上に設定する
- 自動一時停止を透過的に処理する HTTP クライアント(
neon())を使用する - Pool で接続切断エラーを処理する:
import { Pool } from '@neondatabase/serverless';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// CRITICAL: Handle connection termination errors
pool.on('error', (err) => {
console.error('Unexpected database error:', err);
// Implement reconnection logic or alerting
});
- 本番環境の設定:安定したパフォーマンスを得るため、自動一時停止を無効にする
- Neon コンソールで、最小コンピュートユニット数を 0 より大きく設定する
- または、コンピュートサイズを 16 CU 以上にする(スケールゼロが自動的に無効になります)
- トレードオフ:アイドル時間分の料金が発生しますが、安定して 100 ミリ秒未満のクエリを実現できます
- 自動一時停止を無効にしていても、最高のパフォーマンスを得るにはプール接続文字列を使用してください
Issue #12:Drizzle スキーマの不一致
エラー:Property 'x' does not exist on type 'User' などの TypeScript エラー
原因: https://orm.drizzle.team/docs/generate
発生理由:データベースのスキーマが変更されたものの、Drizzle の型が再生成されていません
防止策:スキーマの変更後に npx drizzle-kit generate を実行し、生成されたファイルをコミットしてください。
問題 #13:ブランチ間のマイグレーション競合
エラー:Error: relation "xyz" already exists またはマイグレーションバージョンの競合
参照元:https://neon.tech/docs/guides/branching#schema-migrations
発生する理由:異なるマイグレーション履歴を持つ複数のブランチがあるためです。
予防策:main でマイグレーションを実行した後にブランチを作成するか、マージ前にブランチのスキーマをリセットしてください。
問題 #14:PITR のタイムスタンプが範囲外
エラー:Error: timestamp is outside retention window
参照元:https://neon.tech/docs/introduction/point-in-time-restore
発生する理由:保持期間より古いタイムスタンプから復元しようとしているためです(無料プランでは 7 日間)。
予防策:ご利用のプランの保持期間を確認し、許可された期間内に復元してください。
問題 #15:Prisma に不適切なアダプターを使用
エラー:Error: Invalid connection string またはクエリのパフォーマンスが低下する
参照元:https://www.prisma.io/docs/orm/overview/databases/neon
発生する理由:サーバーレス環境で @prisma/adapter-neon を使用していないためです。
予防策:@prisma/adapter-neon と @neondatabase/serverless をインストールし、HTTP ベースの接続を使用するように Prisma を設定してください。
問題 #16:エッジランタイムでは poolQueryViaFetch が必須
エラー:WebSocket is not defined、または use cache を使用した Next.js 15 のプリレンダリング中にタイムアウトする
参照元:Neon Docs - Prisma Guide | GitHub Issue #181
発生する理由:Cloudflare Workers などのエッジランタイムでは、Pool クエリに WebSocket ではなく HTTP が必要です。Next.js 15 の use cache ディレクティブでは、poolQueryViaFetch を使用して Pool を利用するとタイムアウトすることがあります。
予防策:エッジ環境で Pool を使用する前に、neonConfig.poolQueryViaFetch = true を設定してください。
import { Pool, neonConfig } from '@neondatabase/serverless';
// Enable Pool queries over HTTP fetch (required for edge)
neonConfig.poolQueryViaFetch = true;
const pool = new Pool({ connectionString: env.DATABASE_URL });
export default {
async fetch(request: Request, env: Env) {
// Pool.query() now uses HTTP instead of WebSocket
const result = await pool.query('SELECT * FROM users');
return Response.json(result.rows);
}
};
注意点 — Next.js 15 の use cache:use cache ディレクティブと poolQueryViaFetch = true の併用は避け、代わりに neon() HTTP クライアントを使用してください。
// ❌ Can timeout during prerender
import { Pool, neonConfig } from '@neondatabase/serverless';
neonConfig.poolQueryViaFetch = true;
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
async function getData() {
'use cache';
return await pool.query('SELECT * FROM data');
}
// ✅ Works with prerender
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
async function getData() {
'use cache';
return await sql`SELECT * FROM data`;
}
問題 #17:Node v20 で並列処理を行うとトランザクションコンテキストが失われる
エラー:Promise.all() を使用したトランザクションでの外部キー制約違反:insert or update on table violates foreign key constraint
参照元:Drizzle Issue #2200
発生する理由:Neon のサーバーレスドライバーと Drizzle ORM を使用する Node.js v20 以降では、トランザクション内で Promise.all() を使用してデータベース操作を並列実行すると、トランザクションコンテキストが失われます。順次実行する操作は正しく動作します。これは、Node v20 における Neon ドライバーのセッション処理に固有の、トランザクションコンテキスト管理の問題です。
予防策:順次実行するか、Node.js 環境では postgres-js ドライバー(エッジ非対応)に切り替えてください。
// ❌ FAILS in Node v20 with Neon driver
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values({ name: 'Alice' }).returning();
// Parallel inserts lose transaction context
await Promise.all([
tx.insert(userSettings).values({ userId: user.id, theme: 'dark' }),
tx.insert(userSettings).values({ userId: user.id, locale: 'en' })
]);
// Error: Foreign key constraint violation (user.id not visible)
});
// ✅ WORKS - Sequential execution
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values({ name: 'Alice' }).returning();
await tx.insert(userSettings).values({ userId: user.id, theme: 'dark' });
await tx.insert(userSettings).values({ userId: user.id, locale: 'en' });
});
// ✅ ALTERNATIVE - Use postgres-js driver for Node.js (not edge-compatible)
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const client = postgres(connectionString);
const db = drizzle(client);
// Promise.all() works correctly with this driver
影響を受ける設定:
- Node.js:v20.12.2+
- @neondatabase/serverless:v0.9.0+(v1.0.x まで確認済み)
- drizzle-orm:v0.30.8+
- パターン:トランザクション内の挿入を
Promise.all()で並列実行
問題 #18:サンドボックス化されたランタイムでの process.env へのアクセス
エラー:ReferenceError: process is not defined
参照元:GitHub Issue #179
発生する理由:Neon のサーバーレスドライバーはトップレベルで process.env.* に無条件にアクセスします。そのため、process.env が提供されない Slack の Deno ランタイムなどのサンドボックス化されたランタイムでエラーが発生します。
影響を受ける環境:Slack Deno ランタイム、Node.js の process グローバルを持たないサンドボックス化された JavaScript 環境
予防策:現時点で回避策はありません。次のいずれかを行う必要があります。
- ランタイムで
process.envをポリフィルする - 別の Postgres ドライバーを使用する(Deno 互換の標準
pg) @neondatabase/serverlessとprocess.envにアクセスする基盤の pg ライブラリの両方で、上流の修正を待つ
公式ステータス:修正時期が示されていない未解決の問題です。Neon ドライバーと基盤の pg ライブラリの両方に影響します。
v0.x から v1.0+ への移行
破壊的変更:v1.0.0 では、すべての SQL クエリでタグ付きテンプレート構文が必須です。 出典:Neon ブログ記事 | GitHub Issue #3678
変更前(v0.x):
const result = await sql("SELECT * FROM users WHERE id = $1", [userId]);
変更後(v1.0+):
// Option 1: Tagged template (recommended)
const result = await sql`SELECT * FROM users WHERE id = ${userId}`;
// Option 2: .query() method for parameterized queries
const result = await sql.query("SELECT * FROM users WHERE id = $1", [userId]);
// Option 3: .unsafe() for trusted raw SQL (dynamic identifiers)
const column = 'name';
const result = await sql`SELECT ${sql.unsafe(column)} FROM users`;
変更の理由:v1.0.0 リリースでは、SQL インジェクションの脆弱性を防ぐため、タグ付きテンプレート構文が必須になります。関数呼び出し構文 sql("...", [params]) は、実行時エラーをスローするようになりました。
エラーメッセージ:
This function can now be called only as a tagged-template function:
sql`SELECT ${value}`, not sql("SELECT $1", [value], options)
移行チェックリスト:
- すべての
sql("...", [params])呼び出しをタグ付きテンプレートに置き換える - better-auth を Drizzle と併用している場合は、drizzle-orm を v0.40.1+ にアップグレードする(非互換性を解消します)
- 新しい構文ですべての動的クエリをテストする
- SQL インジェクション防止のパターンを確認する(テンプレートタグが自動的にエスケープします)
better-auth ユーザー:better-auth v1.3.4+ と Neon v1.0.0+ を併用している場合は、互換性を解消するため、drizzle-orm を v0.40.1 以降にアップグレードしてください:
{
"dependencies": {
"@neondatabase/serverless": "^1.0.2",
"better-auth": "^1.3.4",
"drizzle-orm": "^0.40.1"
}
}
代替の回避策:better-auth では Drizzle の代わりに Kysely を使用してください(drizzle-orm の更新なしで動作します):
import { Kysely } from 'kysely';
import { Pool } from '@neondatabase/serverless';
const db = new Kysely({
dialect: new PostgresDialect({
pool: new Pool({ connectionString: process.env.DATABASE_URL })
})
});
パフォーマンスとプロトコルの選択
出典:Neon ブログ - HTTP と WebSocket の比較
HTTP と WebSocket のプロトコルでは、パフォーマンス特性が大きく異なります。選択によって、レイテンシ、スループット、および利用可能な Postgres の機能が変わります。
パフォーマンスのベンチマーク
- HTTP の単一クエリ:初回レイテンシは約 37ms
- WebSocket の初回接続:オーバーヘッドは約 15~20ms
- WebSocket の2回目以降のクエリ:クエリごとに約 4~5ms
- 損益分岐点:連続したクエリが2~3回(WebSocket の方が高速になります)
プロトコル選択マトリックス
| ユースケース | 推奨 | 理由 |
|---|---|---|
| リクエストごとに単一クエリ | HTTP(neon()) | 初回レイテンシが低い(約 37ms) |
| 連続したクエリが2回以上 | WebSocket(Pool/Client) | クエリごとのレイテンシが低い(約 5ms) |
| 並列で実行する独立したクエリ | HTTP | 並列化に適している |
| インタラクティブトランザクション | WebSocket(必須) | トランザクションコンテキストに必要 |
| エッジ関数(単発実行) | HTTP | 接続のオーバーヘッドがない |
| 長時間実行されるワーカー | WebSocket | 接続コストを分散できる |
コード例
// HTTP: Best for single queries
import { neon } from '@neondatabase/serverless';
const sql = neon(env.DATABASE_URL);
const users = await sql`SELECT * FROM users`; // ~37ms
// WebSocket: Best for multiple sequential queries
import { Pool } from '@neondatabase/serverless';
const pool = new Pool({ connectionString: env.DATABASE_URL });
const client = await pool.connect(); // ~15ms setup
try {
const user = await client.query('SELECT * FROM users WHERE id = $1', [1]); // ~5ms
const posts = await client.query('SELECT * FROM posts WHERE user_id = $1', [1]); // ~5ms
const comments = await client.query('SELECT * FROM comments WHERE user_id = $1', [1]); // ~5ms
// Total: ~30ms (vs ~111ms with HTTP)
} finally {
client.release();
}
重要な制限事項
HTTP では次の機能をサポートしません:
- インタラクティブトランザクション(BEGIN/COMMIT/ROLLBACK)
- セッションレベルの機能(一時テーブル、プリペアドステートメント)
- LISTEN/NOTIFY
- COPY プロトコル
エッジ環境での WebSocket の制限事項:
- リクエストをまたいで接続を維持できません
- 1つのリクエストハンドラー内で接続、使用、切断を行う必要があります
設定ファイルのリファレンス
package.json(Neon Direct)
{
"dependencies": {
"@neondatabase/serverless": "^1.0.2"
}
}
package.json(Vercel Postgres)
{
"dependencies": {
"@vercel/postgres": "^0.10.0"
}
}
package.json(Drizzle ORM を使用)
{
"dependencies": {
"@neondatabase/serverless": "^1.0.2",
"drizzle-orm": "^0.44.7"
},
"devDependencies": {
"drizzle-kit": "^0.31.7"
},
"scripts": {
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio"
}
}
drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './db/schema.ts',
out: './db/migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!
}
});
これらの設定の理由:
@neondatabase/serverlessはエッジに対応しています(HTTP/WebSocket ベース)@vercel/postgresは Vercel 上で設定不要で利用できますdrizzle-ormはすべてのランタイム(Cloudflare Workers、Vercel Edge、Node.js)で動作しますdrizzle-kitはマイグレーションとスキーマ生成を処理します
一般的なパターン
パターン1:Neon を使用する Cloudflare Worker
import { neon } from '@neondatabase/serverless';
interface Env { DATABASE_URL: string; }
export default {
async fetch(request: Request, env: Env) {
const sql = neon(env.DATABASE_URL);
const users = await sql`SELECT * FROM users`;
return Response.json(users);
}
};
パターン2:Next.js で Vercel Postgres を使用
'use server';
import { sql } from '@vercel/postgres';
export async function getUsers() {
const { rows } = await sql`SELECT * FROM users`;
return rows;
}
パターン3:Drizzle ORM のセットアップ
// db/index.ts
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
// Usage: Type-safe queries with JOINs
const postsWithAuthors = await db
.select({ postId: posts.id, authorName: users.name })
.from(posts)
.leftJoin(users, eq(posts.userId, users.id));
パターン4:Neon の自動トランザクション
Neon 独自のトランザクション API(配列構文またはコールバック構文)については、ステップ5を参照してください
パターン5:プレビュー環境向けの Neon ブランチ機能
# Create branch for PR
neonctl branches create --project-id my-project --name pr-123 --parent main
# Get connection string for branch
BRANCH_URL=$(neonctl connection-string pr-123)
# Use in Vercel preview deployment
vercel env add DATABASE_URL preview
# Paste $BRANCH_URL
# Delete branch when PR is merged
neonctl branches delete pr-123
# .github/workflows/preview.yml
name: Create Preview Database
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- name: Create Neon Branch
run: |
BRANCH_NAME="pr-${{ github.event.pull_request.number }}"
neonctl branches create --project-id ${{ secrets.NEON_PROJECT_ID }} --name $BRANCH_NAME
BRANCH_URL=$(neonctl connection-string $BRANCH_NAME)
- name: Deploy to Vercel
env:
DATABASE_URL: ${{ steps.branch.outputs.url }}
run: vercel deploy --env DATABASE_URL=$DATABASE_URL
使用する場面:PR またはプレビューのデプロイごとに分離されたデータベースが必要な場合
バンドルされたリソースの使用
スクリプト(scripts/)
setup-neon.sh - Neon データベースを作成し、接続文字列を出力します
chmod +x scripts/setup-neon.sh
./scripts/setup-neon.sh my-project-name
test-connection.ts - データベース接続を検証し、テストクエリを実行します
npx tsx scripts/test-connection.ts
リファレンス(references/)
references/connection-strings.md- 接続文字列の形式、プールあり・なしの完全ガイドreferences/drizzle-setup.md- Neon での Drizzle ORM の段階的なセットアップreferences/prisma-setup.md- Neon アダプターを使用した Prisma のセットアップreferences/branching-guide.md- Neon のデータベースブランチ機能の包括的なガイドreferences/migration-strategies.md- 各種 ORM やツール向けのマイグレーションパターンreferences/common-errors.md- 詳細なトラブルシューティングガイド
Claude が読み込むタイミング:
- 接続の問題をデバッグするときは
connection-strings.mdを読み込みます - ユーザーが Drizzle ORM を使いたい場合は
drizzle-setup.mdを読み込みます - ユーザーが Prisma を使いたい場合は
prisma-setup.mdを読み込みます - ユーザーがプレビュー環境やデータベースブランチ機能について尋ねた場合は
branching-guide.mdを読み込みます - 特定のエラーメッセージが発生した場合は
common-errors.mdを読み込みます
アセット(assets/)
assets/schema-example.sql- users、posts、comments を含むデータベーススキーマの例assets/drizzle-schema.ts- Drizzle スキーマの完全なテンプレートassets/prisma-schema.prisma- Prisma スキーマの完全なテンプレート
高度なトピック
データベースのブランチ機能(Neon 固有の機能)
Neon は Git のようなデータベースのブランチ機能を提供します。
# Create branch from main
neonctl branches create --name dev --parent main
# Create from point-in-time (PITR restore)
neonctl branches create --name restore --parent main --timestamp "2025-10-28T10:00:00Z"
# Get connection string for branch
neonctl connection-string dev
# Delete branch
neonctl branches delete feature
主な機能:
- コピーオンライト:ブランチは即座に作成されます(データのコピーは不要です)
- プレビューのデプロイ:PR ごとにブランチを作成し、マージ時に削除します
- ポイントインタイムリストア:特定のタイムスタンプに復元します(無料プランでは 7 日間保持)
- コンピューティングの共有:ブランチ間でコンピューティングの上限を共有します(無料プラン)。有料プランでは個別にコンピューティングを利用できます
パフォーマンスとセキュリティに関する注意事項
接続プールの監視:
- Neon ダッシュボードで使用状況を確認します(接続上限:無料プランでは 100、プーリング使用時は約 10,000)
- 使用率が 80% を超えた場合にアラートを設定します
- 「接続プールが枯渇しました」エラーを避けるには、プール済み接続文字列を使用します
クエリの最適化:
- 頻繁にクエリする列にはインデックスを使用します
- N+1 クエリを避けます(JOIN または Drizzle のリレーションを使用します)
- 繰り返し実行するクエリには Drizzle のプリペアドステートメントを使用します
セキュリティ:
- 接続文字列をコードに直接記述しないでください(環境変数を使用します)
- テンプレートタグ構文により SQL インジェクションを防止できます
- マルチテナントアプリでは行レベルセキュリティ(RLS)を使用します
- クエリの前に Zod で入力を検証します
依存関係
必須:
@neondatabase/serverless@^1.0.2- Neon のサーバーレス Postgres クライアント(HTTP/WebSocket ベース)@vercel/postgres@^0.10.0- Vercel Postgres クライアント(Neon を直接使用する場合の代替、Vercel 固有)
任意:
drizzle-orm@^0.44.7- TypeScript ORM(エッジ対応、推奨)drizzle-kit@^0.31.7- Drizzle のスキーママイグレーションおよびイントロスペクション@prisma/client@^6.10.0- Prisma ORM(Node.js 専用、エッジ非対応)@prisma/adapter-neon@^6.10.0- Neon サーバーレス用 Prisma アダプターneonctl@^2.19.0- データベース管理用 Neon CLIzod@^3.24.0- 入力サニタイズ用のスキーマ検証
公式ドキュメント
- Neon ドキュメント:https://neon.tech/docs
- Neon Serverless パッケージ:https://github.com/neondatabase/serverless
- Vercel Postgres:https://vercel.com/docs/storage/vercel-postgres
- Vercel Storage(すべて):https://vercel.com/docs/storage
- Neon ブランチ機能ガイド:https://neon.tech/docs/guides/branching
- Neonctl CLI:https://neon.tech/docs/reference/cli
- Drizzle + Neon:https://orm.drizzle.team/docs/quick-postgresql/neon
- Prisma + Neon:https://www.prisma.io/docs/orm/overview/databases/neon
- Context7 ライブラリ ID:
/github/neondatabase/serverless、/github/vercel/storage
パッケージバージョン(2026-01-09 確認済み)
{
"dependencies": {
"@neondatabase/serverless": "^1.0.2",
"@vercel/postgres": "^0.10.0",
"drizzle-orm": "^0.45.1"
},
"devDependencies": {
"drizzle-kit": "^0.31.8",
"neonctl": "^2.19.0"
}
}
最新の Prisma(必要な場合):
{
"dependencies": {
"@prisma/client": "^6.10.0",
"@prisma/adapter-neon": "^6.10.0"
},
"devDependencies": {
"prisma": "^6.10.0"
}
}
本番環境での実績
このスキルは、Neon と Vercel Postgres の本番環境へのデプロイに基づいています。
- Cloudflare Workers:API で毎日 50K 件以上のリクエストを処理し、接続エラーは 0 件
- Vercel Next.js App:月間ユーザー数 100K 人以上の E コマースサイト
- ビルド時間:5 分未満(初期セットアップ)、30 秒未満(デプロイ)
- エラー:0 件(既知の問題 19 件をすべて防止)
- 検証項目:✅ 接続プーリング、✅ SQL インジェクションの防止、✅ トランザクション処理、✅ ブランチを活用したワークフロー、✅ エッジランタイムとの互換性、✅ Node v20 のトランザクションパターン
トラブルシューティング
問題:Error: connection pool exhausted
解決方法:
- プーリングされた接続文字列(末尾が
-pooler.region.aws.neon.tech)を使用していることを確認します - Neon ダッシュボードで接続の使用状況を確認します
- 制限に頻繁に達する場合は、上位のプランにアップグレードします
- クエリを最適化して、接続の保持時間を短縮します
問題:Error: TCP connections are not supported
解決方法:
pgやpostgres.jsではなく、@neondatabase/serverlessを使用します- 従来の Postgres クライアントをインポートしていないことを確認します
- バンドルに HTTP/WebSocket ベースのクライアントが含まれていることを確認します
問題:Error: database "xyz" does not exist
解決方法:
DATABASE_URLが正しいデータベースを指していることを確認します- Neon のブランチ機能を使用している場合は、ブランチがまだ存在することを確認します
- 接続文字列の形式を確認します(タイプミスがないこと)
問題:コールドスタート時にクエリが遅い
解決方法:
- Neon は無料プランでは、5 分間操作がないと自動的に一時停止します
- 再開後の最初のクエリには約 1~2 秒かかります
- コールドスタートを考慮し、クエリのタイムアウトを 10 秒以上に設定します
- 常時稼働するデータベースには、有料プランで自動一時停止を無効にします
問題:PrismaClient is unable to be run in the browser
解決方法:
- Prisma は Cloudflare Workers(V8 アイソレート)では動作しません
- エッジに対応した ORM として Drizzle ORM を使用します
- Prisma は
@prisma/adapter-neonを使うと、Vercel Edge/Node.js ランタイムで動作します
問題:ブランチ間でのマイグレーションバージョンの競合
解決方法:
- まずメインブランチでマイグレーションを実行します
- マイグレーションの後にフィーチャーブランチを作成します
- または、マージの前にブランチのスキーマをリセットします:
neonctl branches reset feature --parent main
問題:drizzle-kit での「WebSocket warning」(コミュニティからの情報)
エラー:Warning: @neondatabase/serverless can only connect to remote Neon/Vercel Postgres/Supabase instances through a websocket
出典:GitHub Discussion #12508
解決方法:この警告は情報提供を目的としたもので、安全に無視できます。警告が出てもマイグレーションは正常に動作します。この警告は、WebSocket プロトコルが使用されていることをユーザーに知らせるもので、問題が発生した場合のデバッグに役立ちます。開発依存関係として pg を追加すれば警告は表示されなくなりますが、追加する必要はありません。
問題:VPN によって Neon への接続がブロックされる(コミュニティからの情報)
エラー:NeonDbError: Error connecting to database: fetch failed [cause]: SocketError: other side closed
出典:GitHub Issue #146 のコメント
解決方法:一部の VPN は、Neon のエンドポイントへの WebSocket 接続または fetch 接続をブロックします。これは主に、Next.js 14 以降のサーバーアクションを使った開発中(localhost)に発生します。VPN を無効にしてテストするか、VPN の設定で Neon のドメインを許可リストに追加します:
# Whitelist these domains in VPN:
*.neon.tech
*.aws.neon.tech
セットアップ完了チェックリスト
このチェックリストを使って、セットアップを確認してください。
- パッケージがインストールされている(
@neondatabase/serverlessまたは@vercel/postgres) - Neon データベースが作成されている(または Vercel Postgres がプロビジョニングされている)
- プーリング接続文字列を取得している(末尾が
-pooler.) - 接続文字列に
?sslmode=requireが含まれている - 環境変数が設定されている(
DATABASE_URLまたはPOSTGRES_URL) - データベーススキーマが作成されている(生の SQL、Drizzle、または Prisma)
- クエリでテンプレートタグ構文(
sql`...`)を使用している - トランザクションで適切な try/catch を使用し、接続を解放している
- 接続プーリングを確認している(プーリング接続文字列を使用)
- ランタイムに適した ORM を選択している(エッジには Drizzle、Node.js には Prisma)
- 開発用データベースでローカルテストを実施している
- 本番環境/プレビュー環境にデプロイし、テストしている
- Neon ダッシュボードで接続のモニタリングを設定している
質問や問題がありますか?
- 詳しいトラブルシューティングについては、
references/common-errors.mdを確認してください - 7 段階のセットアッププロセスのすべての手順を確認してください
- 公式ドキュメントを確認してください:https://neon.tech/docs
- サーバーレス環境では、必ずプーリング接続文字列を使用してください
- 接続文字列に
sslmode=requireが含まれていることを確認してください scripts/test-connection.tsで接続をテストしてください
最終確認日:2026-01-21 | スキルのバージョン:2.0.0 | 変更内容:新たな問題を 4 件追加(#16~#19:poolQueryViaFetch、Node v20 のトランザクション、process.env のサンドボックス化);移行ガイド(v0.x→v1.0+)を追加;パフォーマンスとプロトコル選択のセクションを追加;問題 #11 に自動一時停止の処理と本番環境の設定を追加;コミュニティから寄せられたトラブルシューティング(WebSocket の警告、VPN によるブロック)をティア 2 に追加