Skip to content

fix: Supabase Transaction pool mode 対応のため DB ドライバを postgres-js に切り替え#146

Merged
KinjiKawaguchi merged 1 commit intomainfrom
refactor/drizzle-postgres-js
Apr 5, 2026
Merged

fix: Supabase Transaction pool mode 対応のため DB ドライバを postgres-js に切り替え#146
KinjiKawaguchi merged 1 commit intomainfrom
refactor/drizzle-postgres-js

Conversation

@KinjiKawaguchi
Copy link
Copy Markdown
Member

@KinjiKawaguchi KinjiKawaguchi commented Apr 4, 2026

Summary

DB ドライバを drizzle-orm/node-postgres (pg) から Supabase 推奨の drizzle-orm/postgres-js (postgres) に切り替える。

Why

1. Transaction pool mode 対応

Supabase の Transaction pool mode(pgbouncer 経由、port 6543)は prepared statement をサポートしない。postgres-jsprepare: false オプションを明示することで、プーラー経由の接続で安全にクエリを実行できる。

2. SSL 証明書検証の問題

最新の pg (v8.x 系の後期) は sslmode=requireverify-full のエイリアスとして扱うようになり、Supabase の証明書チェーンで SELF_SIGNED_CERT_IN_CHAIN エラーが発生するようになった。これにより its-discord から接続できなくなる問題が発生していた。

postgres-js はこの挙動に影響されず、プーラー URL でそのまま接続できる。

3. Supabase 公式推奨

Supabase の Drizzle 接続ドキュメントでも postgres-js + prepare: false が推奨されている。

Changes

  • drizzle-orm/node-postgresdrizzle-orm/postgres-js
  • 依存パッケージ: pg / @types/pg を削除し postgres を追加
  • src/infrastructure/drizzle/client.tspostgres(connectionString, { prepare: false }) を使用

Test plan

  • npm run build が成功すること
  • its-discord から getMemberByDiscordId が正常動作することを確認
  • 既存のテストが通ること

Open with Devin

Copilot AI review requested due to automatic review settings April 4, 2026 16:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

DB ドライバを drizzle-orm/node-postgrespg)から drizzle-orm/postgres-jspostgres)へ切り替え、Supabase の Transaction pool mode(prepared statement 非対応)や SSL 証明書検証まわりの接続問題を回避するための変更です。

Changes:

  • Drizzle の接続先を node-postgres ベースから postgres-js ベースへ変更
  • postgres(connectionString, { prepare: false }) を使用するように接続初期化を更新
  • 依存関係を pg/@types/pg から postgres に置き換え

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated no comments.

File Description
src/infrastructure/drizzle/client.ts Drizzle クライアント生成を postgres-js に切り替え、prepared statement を無効化
package.json pg/@types/pg を削除し postgres を追加
package-lock.json ロックファイル上の依存関係を postgres へ更新(pg 関連を削除)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment on lines 19 to 21
export function getDb() {
return drizzle(getPool(), { schema });
return drizzle(getClient(), { schema });
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: getDb() creates a new drizzle instance on every call

Each call to getDb() at src/infrastructure/drizzle/client.ts:19-21 creates a new drizzle() wrapper instance, even though the underlying postgres client is cached. This is the same pattern that existed before the migration (with drizzle(getPool(), { schema })), so it's not a regression. However, drizzle-orm recommends creating the drizzle instance once. The drizzle wrapper is lightweight, so this is unlikely to cause measurable performance issues, but caching the drizzle instance (similar to how client is cached) would be a minor improvement.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread src/infrastructure/drizzle/client.ts Outdated
import * as schema from "./schema";

let pool: Pool | null = null;
let client: ReturnType<typeof postgres> | null = null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: No graceful shutdown hook for postgres.js client

The postgres client cached at src/infrastructure/drizzle/client.ts:5 is never explicitly closed (e.g., via client.end()). The old pg.Pool had the same issue — neither implementation registered a shutdown hook. With postgres.js, the client maintains a connection pool internally. In serverless or short-lived contexts this is fine (connections close on process exit), but in long-running services a graceful shutdown hook calling client.end() would be best practice. This is pre-existing and not introduced by this PR.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 0 new potential issues.

Open in Devin Review

@KinjiKawaguchi KinjiKawaguchi force-pushed the refactor/drizzle-postgres-js branch 3 times, most recently from 59a21b6 to 49ca25c Compare April 5, 2026 04:34
@KinjiKawaguchi KinjiKawaguchi changed the title refactor: DB ドライバを node-postgres から postgres-js に切り替え fix: Supabase Transaction pool mode 対応のため DB ドライバを postgres-js に切り替え Apr 5, 2026
Supabase の Transaction pool mode(pgbouncer)は prepared statement を
サポートしないため、Supabase が推奨する postgres-js + prepare:false の
構成に統一する。また node-postgres は最新の pg v9 で sslmode=require を
verify-full として扱うようになり、Supabase の証明書チェーンで接続失敗する
問題があったが、postgres-js ではこの問題が発生しない。

- drizzle-orm/node-postgres → drizzle-orm/postgres-js に変更
- pg / @types/pg 依存を削除し postgres を追加
- prepare: false を明示して Transaction pool mode に対応

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment on lines +15 to 16
client = postgres(connectionString, { prepare: false });
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: prepare: false disables prepared statements globally

Setting prepare: false at src/infrastructure/drizzle/client.ts:16 disables prepared statements for all queries through this client. The comment correctly explains this is needed for Supabase's Transaction pool mode (pgbouncer in transaction mode doesn't support prepared statements). This is a correct and necessary configuration choice, but it does mean a slight performance trade-off — repeated identical queries won't benefit from server-side prepared statement caching. Worth noting for anyone reading this code that this is an intentional trade-off, not an oversight.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants