api-database-vercel-postgres について
非推奨となった `@vercel/postgres` を使うプロジェクトの保守と移行向けのコーディングスキルです。接続プールや直接接続、クエリ、エッジランタイムの注意点を扱い、新規プロジェクトには `@neondatabase/serverless` を推奨します。
主な機能
- 01`sql` によるパラメーター化クエリ
- 02複数クエリのセッションとトランザクション
- 03カスタムプールとクライアントの設定
- 04プール接続と直接接続の使い分け
- 05エッジランタイムの接続パターン
- 06`@neondatabase/serverless` への移行ガイド
活用シーン
- 01既存の `@vercel/postgres` アプリの保守
- 02Vercel のエッジまたはサーバーレス関数での DB クエリ
- 03同一セッションで複数のクエリを実行
- 04アプリを `@neondatabase/serverless` に移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents-inc/ skills --skill api-database-vercel-postgresインストール後、Claude Code で/api-database-vercel-postgresを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.yamlreference.mdexamples/core.md詳細
Vercel Postgres のパターン
クイックガイド:
@vercel/postgresは@neondatabase/serverlessを薄くラップしたもので、POSTGRES_URL環境変数から自動的に接続します。単発のクエリにはsqlタグ付きテンプレートを使用します(エッジに対応し、自動でプールされます)。複数のクエリを実行する場合は、sql.connect()を使用してクライアントを取得します。エッジランタイムでは、リクエスト間で接続を再利用できません(maxUses: 1)。このパッケージは非推奨です(2024年12月)。新しいプロジェクトでは、@neondatabase/serverlessを直接使用してください。
<critical_requirements>
重要:このスキルを使用する前に
すべてのコードは CLAUDE.md のプロジェクト規約に従う必要があります(kebab-case、名前付きエクスポート、インポート順序、
import type、名前付き定数)
(単発のクエリには、必ず sql タグ付きテンプレートを使用してください。POSTGRES_URL から自動的に接続し、プーリングを処理します)
(sql.connect() または pool.connect() で取得したクライアントを使用した後は、必ず client.release() を呼び出してください。クライアントがリークすると、接続プールが枯渇します)
(プール接続には必ず POSTGRES_URL を、直接接続には POSTGRES_URL_NON_POOLING を使用してください。これらを混在させると、接続の枯渇や機能の制限が発生します)
(@vercel/postgres は非推奨であることを必ず明記し、新しいプロジェクトには @neondatabase/serverless を推奨してください)
</critical_requirements>
自動検出: @vercel/postgres、vercel postgres、POSTGRES_URL、POSTGRES_URL_NON_POOLING、sql tagged template vercel、createPool vercel、createClient vercel、VercelPool、VercelClient
使用する場面:
- すでに
@vercel/postgresを使用している既存プロジェクトの保守 - Vercel のエッジ/サーバーレス関数から Postgres にクエリを実行する場合
- 環境変数から自動接続する、シンプルなデータベースアクセス
@vercel/postgresから@neondatabase/serverlessへの移行
対象となる主なパターン:
sqlタグ付きテンプレート(自動プーリング、エッジ対応、単発のクエリ)- 複数のクエリを実行するクライアントセッション用の
sql.connect() - カスタム設定用の
createPool()/createClient() - 環境変数(
POSTGRES_URL、POSTGRES_URL_NON_POOLING) - エッジランタイムと Node.js ランタイムの違い
@neondatabase/serverlessへの移行方法
使用しない場面:
- 新しいプロジェクト(
@neondatabase/serverlessを直接使用してください) - 接続を維持する長時間稼働のサーバープロセス(標準の
pgドライバーを使用してください) - 一般的な PostgreSQL クエリ構文(SQL/Postgres のスキルを使用してください)
詳細なリソース:
- 判断フレームワークと早見表については、reference.md を参照してください
例:
- examples/core.md -- sql タグ付きテンプレート、createPool、createClient、エッジのパターン、移行
## 設計方針
@vercel/postgres は、Vercel にデプロイされたアプリケーションの接続管理を簡素化する @neondatabase/serverless の便利なラッパーです。POSTGRES_URL/POSTGRES_URL_NON_POOLING 環境変数(Vercel Marketplace 連携によって自動的にプロビジョニングされます)から接続文字列を読み取るため、接続文字列を手動で作成する必要はありません。
基本原則:
- 設定不要の接続 --
sqlエクスポートは環境変数から自動接続します。コード内で接続文字列を設定する必要はありません。 - タグ付きテンプレートの安全性 --
sqlは関数ではなく、タグ付きテンプレートリテラルです。パラメーターは自動的にパラメーター化され、SQL インジェクションを防ぎます。 - デフォルトでコネクションプーリング --
sqlとcreatePool()はプール済み接続文字列(POSTGRES_URL)を使用します。createClient()は直接接続文字列(POSTGRES_URL_NON_POOLING)を使用します。 - エッジ環境への対応 -- エッジランタイムでは、IO 接続がリクエスト間で維持できないため、SDK は
maxUses: 1を設定します。1 つのリクエスト内で複数のクエリを実行するには、sql.connect()を使用します。
非推奨化の背景:
Vercel Postgres は 2024 年 12 月にサービスを終了しました。すべてのデータベースは Neon に移行されました。npm パッケージ @vercel/postgres(v0.10.0)はメンテナンスされていません。移行先:
- 全面的な移行(推奨):
@neondatabase/serverless(現在も開発が続いており、HTTP トランザクションや合成可能なフラグメントを備えた、より豊富な API を提供)
基本パターン
パターン 1:sql による単発クエリ
sql エクスポートは POSTGRES_URL から自動接続するタグ付きテンプレートです。値は自動的にパラメーター化され、SQL インジェクションを防ぎます。適切な例と不適切な例を比較した詳しいサンプルについては、examples/core.md を参照してください。
import { sql } from "@vercel/postgres";
const ACTIVE_STATUS = "active";
const { rows } =
await sql`SELECT id, name FROM users WHERE status = ${ACTIVE_STATUS}`;
パターン 2:sql.connect() による複数クエリのセッション
同じ接続で複数のクエリを実行する必要がある場合(トランザクションや順次処理など)は、クライアントを取得します。単独の sql 呼び出しごとに異なるプール接続が使われる可能性があるため、別々の sql 呼び出しで BEGIN/COMMIT を実行しても、実際のトランザクションにはなりません。トランザクションのパターンについては、examples/core.md を参照してください。
const client = await sql.connect();
try {
await client.sql`BEGIN`;
// ... queries on same client ...
await client.sql`COMMIT`;
} catch (error) {
await client.sql`ROLLBACK`;
throw error;
} finally {
client.release();
}
パターン 3:カスタムプールとクライアント
カスタム接続文字列(セカンダリデータベース)には createPool() を使用します。マイグレーションやセッションレベルの機能で必要となる直接接続(非プール接続)には createClient() を使用します。詳しいサンプルについては、examples/core.md を参照してください。
import { createPool } from "@vercel/postgres";
const pool = createPool({
connectionString: process.env.SECONDARY_POSTGRES_URL,
});
const { rows } =
await pool.sql`SELECT id, title FROM posts WHERE published = true`;
パターン 4:エッジランタイムに関する考慮事項
エッジランタイムでは、接続をリクエスト間で再利用できないため、SDK は maxUses: 1 を設定します。単一の sql 呼び出しは問題なく動作しますが、複数のクエリを実行する場合は、1 つの接続を共有するために sql.connect() を使用します。エッジ固有のパターンについては、examples/core.md を参照してください。
パターン 5:@neondatabase/serverless への移行
@vercel/postgres は非推奨のため、@neondatabase/serverless に移行してください。詳しい移行例については、examples/core.md を参照してください。
把握しておくべき主な違い:
@vercel/postgresは{ rows, rowCount, ... }を返します。一方、@neondatabase/serverlessのneon()は行を直接返します(fullResults: trueの場合を除く)。@vercel/postgresはPOSTGRES_URLを読み取ります。一方、@neondatabase/serverlessでは接続文字列(通常はDATABASE_URL)を明示的に指定する必要があります。@neondatabase/serverlessは、sql.transaction()による HTTP トランザクションと、合成可能なフラグメントを追加します。
<decision_framework>
選択の指針
使用する API
What kind of operation?
+-- Single query (SELECT, INSERT, UPDATE, DELETE)
| +-- Use sql tagged template directly
+-- Multiple queries that must be atomic (transaction)?
| +-- Use sql.connect() to get a client, wrap in BEGIN/COMMIT
+-- Need custom connection string (not POSTGRES_URL)?
| +-- Use createPool() with explicit connectionString
+-- Need session-level features (SET, LISTEN/NOTIFY)?
| +-- Use createClient() (reads POSTGRES_URL_NON_POOLING)
+-- Starting a new project?
+-- Use @neondatabase/serverless instead
環境変数の選択
What is the workload?
+-- Serverless/edge function --> POSTGRES_URL (pooled)
+-- Application queries --> POSTGRES_URL (pooled)
+-- Schema migrations --> POSTGRES_URL_NON_POOLING (direct)
+-- LISTEN/NOTIFY --> POSTGRES_URL_NON_POOLING (direct)
+-- pg_dump / pg_restore --> POSTGRES_URL_NON_POOLING (direct)
</decision_framework>
<red_flags>
注意すべき重大な問題
優先度の高い問題:
sql.connect()を使わずにsqlでトランザクションを実行する —sqlタグ付きテンプレートの呼び出しごとに、異なるプール接続が使われる可能性があります。ある接続で BEGIN を実行し、別の接続で COMMIT を実行しても、トランザクションはまったく成立しません。sql.connect()の後にclient.release()を呼び忘れる — クライアントがリークすると接続プールが使い尽くされ、その後のすべてのクエリがタイムアウトするまでハングします。- マイグレーションに
POSTGRES_URLを使う — プール接続はトランザクションモードの PgBouncer 経由で実行されるため、マイグレーションツールに必要なセッションレベルの機能が使えなくなります。
優先度が中程度の問題:
- タグ付きテンプレートの代わりに文字列補間を使う —
sql`...${value}...`は安全です。sql.query(\...${value}...`)` は SQL インジェクションにつながります。 - プールやクライアントを閉じずに作成する —
createClient()では明示的なclient.end()が必要です。呼び忘れると接続がリークします。 - 非推奨であることを無視する —
@vercel/postgresv0.10.0 が最後のバージョンです。セキュリティパッチやバグ修正はリリースされません。
注意点とエッジケース:
- エッジランタイムの
maxUses: 1— エッジでは、プールは1つのリクエスト内で接続を再利用できません。複数のsql呼び出しを実行すると、それぞれで新しい接続が開かれます。1つの接続を共有するにはsql.connect()を使ってください。 sqlは関数ではなくタグ付きテンプレートです —sql(...)は誤りです。sql`...`が正しい書き方です。Vercel 以外の Postgres の例からコピーする際によくある間違いです。POSTGRES_URLとDATABASE_URLの違い —@vercel/postgresはデフォルトでPOSTGRES_URLを読み取ります。@neondatabase/serverlessはデフォルトでは何も読み取らないため、明示的に渡してください。Neon への移行後、Vercel は両方を設定しますが、コードでは SDK の想定に合わせる必要があります。- PgBouncer のトランザクションモードの制限 — プール接続経由では、SET/RESET、LISTEN/NOTIFY、PRESERVE 付きの一時テーブル、セッションレベルのアドバイザリロックは使用できません。
@neondatabase/serverlessとは結果の形式が異なる —@vercel/postgresは{ rows, rowCount, fields }を返します。Neon のneon()関数は行を直接返します。この違いを考慮しないと、移行中にコードが動作しなくなります。
</red_flags>
<critical_reminders>
重要な注意事項
すべてのコードは CLAUDE.md のプロジェクト規約に従う必要があります(kebab-case、名前付きエクスポート、インポート順序、
import type、名前付き定数)
(単発のクエリには必ず sql タグ付きテンプレートを使ってください。POSTGRES_URL から自動接続し、接続プールを処理します)
(sql.connect() または pool.connect() で取得したクライアントを使用した後は、必ず client.release() を呼び出してください。クライアントがリークすると接続プールが使い尽くされます)
(プール接続には必ず POSTGRES_URL を、直接接続には POSTGRES_URL_NON_POOLING を使ってください。これらを混在させると、接続枯渇や機能制限が発生します)
(@vercel/postgres が非推奨であることを明記してください。新規プロジェクトには @neondatabase/serverless を推奨してください)
これらのルールに従わないと、接続プールの枯渇、SQL インジェクションの脆弱性、または気付かれないトランザクション失敗が発生します。
</critical_reminders>