Aurora PostgreSQL Serverless エクスプレス設定に Drizzle ORM で接続してみた
Aurora PostgreSQL Serverless のエクスプレス設定(以下、Aurora Expressと呼びます)という、VPC不要・数秒でクラスターが作れる新しいAuroraのオプションが登場しました。
弊社ブログでも紹介しているので詳しくはこちらをご覧ください。
今回は、このAurora ExpressにTypeScript製ORM「Drizzle ORM」を使って接続してみました。IAM認証によるトークンの扱いがポイントになります。
なお、Aurora DSQLでもdrizzleを使った接続記事があります。今回のExpressとはサービスが異なりますのでご注意ください。
最初に結論
- Aurora Express は IAM認証のみ をサポート
- Drizzleから接続するには、
@aws-sdk/rds-signerでIAM認証トークンを生成してパスワードとして渡す node-postgres(pg)の接続設定にasync関数を渡すことで、接続ごとにIAM認証トークンを動的に取得できる- 通常のAurora PostgreSQLなので
generatedAlwaysAsIdentity()等のDrizzle標準機能がそのまま使える
Aurora PostgreSQL Serverless エクスプレス設定とは
Aurora Expressは、Auroraの新しいクラスター作成オプションです。
最大の特徴は、 Internet Access Gatewayが組み込まれていて、VPCなしでインターネットから直接接続できる 点です。
個人開発やプロトタイプ用途にとても使いやすそうです。
前提
以下は完了している前提で進めます。
- Aurora Express クラスターの作成
- AWS CLIのインストールとAWS認証情報の設定
今回作るもの
Hono + Drizzle ORM を使ったシンプルなREST APIサーバーを作成します。
サンプルコード全体はGitHubで公開しています。
ディレクトリ構成は以下の通りです。
.
├── src/
│ ├── db/
│ │ ├── index.ts # DB接続設定(IAM認証トークンの取得・キャッシュ)
│ │ └── schema.ts # スキーマ定義
│ ├── app.ts # Honoルート定義
│ ├── logger.ts # ロガー設定(pino)
│ └── index.ts # サーバー起動
├── scripts/
│ └── migrate.sh # マイグレーション用スクリプト
├── drizzle.config.ts
├── .env
└── package.json
動かしてみる
依存パッケージのインストール
まずは依存パッケージのインストールをします。
$ npm install
.envの設定
.env.example をコピーしてください。
$ cp .env.example .env
.env ファイルに自分の環境に合わせてDB接続情報を設定します。DB_HOST にはクラスターのエンドポイントを指定してください。
DB_HOST=my-express-cluster.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
DB_USER=postgres
DB_NAME=postgres
AWS_REGION=ap-northeast-1
TZ=Asia/Tokyo
マイグレーション
npm run db:migrate を実行してテーブルを作成します。
コマンド実行には、IAM認証トークンを生成するため AWS CLI のインストールと AWSの認証情報 が必要です。
- 参考:コマンドラインから IAM 認証を使用して DB インスタンスに接続する: AWS CLI および psql クライアント - Amazon Relational Database Service
$ npm run db:migrate
IAM認証トークンを生成中...
マイグレーションを実行中...
[✓] Changes applied
APIサーバーの起動と動作確認
サーバーを起動します。
ここでも、IAM認証トークンを生成するため AWSの認証情報 が必要です。環境変数等に設定してください。
$ npm start
[HH:MM:SS.mmm] INFO (XXXXX): [db/index] module initialized
[HH:MM:SS.mmm] INFO (XXXXX): http://localhost:3000
...
curl コマンドでAPIのCRUDを確認します。
# POST - ユーザー作成
$ curl -s -X POST http://localhost:3000/users \
-H 'Content-Type: application/json' \
-d '{"name":"クラスメソ太","email":"mesota@example.com"}'
{
"id": 1,
"name": "クラスメソ太",
"email": "mesota@example.com",
"createdAt": "2025-03-26T10:00:00.000Z"
}
# GET - 一覧取得
$ curl -s http://localhost:3000/users
[
{ "id": 1, "name": "クラスメソ太", "email": "mesota@example.com", "createdAt": "2025-03-26T10:00:00.000Z" }
]
# PUT - 更新
$ curl -s -X PUT http://localhost:3000/users/1 \
-H 'Content-Type: application/json' \
-d '{"name":"クラスメソ次郎"}'
{
"id": 1,
"name": "クラスメソ次郎",
"email": "mesota@example.com",
"createdAt": "2025-03-26T10:00:00.000Z"
}
# DELETE - 削除
$ curl -s -X DELETE http://localhost:3000/users/1
{
"id": 1,
"name": "クラスメソ次郎",
"email": "mesota@example.com",
"createdAt": "2025-03-26T10:00:00.000Z"
}
問題なく動作しました!
実装のポイント
ここからは、通常のdrizzle + PostgreSQL接続と異なる点を中心に解説します。
IAM認証トークンの取得とキャッシュ(src/db/index.ts)
Aurora Expressは IAM認証のみ をサポートしているため、パスワードの代わりにIAM認証トークンを使います。
トークンは @aws-sdk/rds-signer の Signer クラスで生成できます。
- 参考:SDK for JavaScript (v3) を使用した Amazon RDS の例 - AWS SDK for JavaScript
- 参考:@aws-sdk/rds-signer - AWS SDK for JavaScript v3
const signer = new Signer({
hostname: DB_HOST,
port: 5432,
username: DB_USER,
region: AWS_REGION,
});
pg.Pool の password オプションにはasync関数を渡せます。これを利用して、新しい接続が確立されるたびにトークンを取得する関数を渡します。
const pool = new Pool({
host: DB_HOST,
// ...
ssl: true, // SSLは必須
password: () => signer.getAuthToken(),
});
ただし、IAM認証トークンは有効期限が 15分 です。接続のたびに毎回AWS APIを叩くのは無駄なので、有効期限内はトークンをキャッシュし、期限の5分前になったタイミングで再取得するようにします。
また、pg.Pool の idleTimeoutMillis(アイドル接続を破棄するまでの時間)はデフォルト10秒です。
接続処理に時間がかかるため、長めに設定を変えました。
Aurora Serverlessがサーバー側でアイドル接続を約5分で切断するため、それより短い 4分 に設定しています。
const TOKEN_TTL_MS = 15 * 60 * 1000;
const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;
let cachedToken = "";
let tokenExpiresAt = 0;
async function getAuthToken(): Promise<string> {
if (Date.now() < tokenExpiresAt - TOKEN_REFRESH_BUFFER_MS) {
return cachedToken;
}
cachedToken = await signer.getAuthToken();
tokenExpiresAt = Date.now() + TOKEN_TTL_MS;
return cachedToken;
}
const pool = new Pool({
host: DB_HOST,
// ...
ssl: true,
idleTimeoutMillis: 4 * 60 * 1000, // Aurora側の切断(約5分)より短く設定
password: getAuthToken,
});
export const db = drizzle({ client: pool });
マイグレーション: トークンをURLに埋め込む(scripts/migrate.sh)
drizzle-kit はマイグレーション実行時に DATABASE_URL を参照します。通常のパスワード認証であれば .env にURLを固定値で書いておけばよいのですが、IAM認証トークンは都度生成が必要なため、実行時にトークンを取得してURLを組み立てる必要があります。
トークンにはURLで使えない文字(/, +, = など)が含まれるため、URLエンコードも必要です。
この手順をまとめたのが scripts/migrate.sh です。
scripts/migrate.sh
#!/bin/bash
set -e
# .env から環境変数を読み込む
if [ -f .env ]; then
export $(grep -v '^#' .env | grep -v '^$' | xargs)
fi
# 必須変数チェック
: "${DB_HOST:?DB_HOST が .env に設定されていません}"
: "${DB_USER:=postgres}"
: "${DB_NAME:=postgres}"
: "${AWS_REGION:=ap-northeast-1}"
echo "IAM認証トークンを生成中..."
TOKEN=$(aws rds generate-db-auth-token \
--hostname "$DB_HOST" \
--port 5432 \
--region "$AWS_REGION" \
--username "$DB_USER")
ENCODED_TOKEN=$(node -e "process.stdout.write(encodeURIComponent(process.argv[1]))" "$TOKEN")
export DATABASE_URL="postgresql://${DB_USER}:${ENCODED_TOKEN}@${DB_HOST}:5432/${DB_NAME}?sslmode=require"
echo "マイグレーションを実行中..."
npx drizzle-kit push
スキーマ定義(src/db/schema.ts)
スキーマ定義は通常のDrizzle + PostgreSQLと同じです。改めて確認しておきます。
import { integer, pgTable, varchar, timestamp } from "drizzle-orm/pg-core";
export const usersTable = pgTable("users", {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).notNull().unique(),
createdAt: timestamp().defaultNow().notNull(),
});
generatedAlwaysAsIdentity() はPostgreSQLのシーケンスを使った自動採番です。Express Configurationは通常のAurora PostgreSQLなので、こういったdrizzle標準の機能がそのまま使えます。
実行時間をログで見る
実際どれくらい実行時間がかかるのか、実行ログで見てみましょう。
Aurora Expressはデフォルト300秒(5分)の非アクティブで自動停止します。コールドスタート時にどのくらい時間がかかるか、実際のログで確認します。
一番最初のコールドスタート状態から接続を試みて、接続が確立するまで 約16秒 かかっています。
[16:20:54.070] INFO (99349): [db] query: select "id", "name", "email", "createdAt" from "users" -- []
[16:20:55.137] INFO (99349): [getAuthToken] called
[16:20:55.137] INFO (99349): [getAuthToken] cache miss. fetching new token...
[16:20:55.155] INFO (99349): [getAuthToken] token fetched. expires at 2026-03-27T16:35:55
[16:21:10.336] INFO (99349): [pool] new connection established
[16:21:10.478] INFO (99349): [db] result: 1 rows
接続が確立できた後は ミリ秒 レベルで応答が返ってきています。
[16:22:03.743] INFO (99349): [db] query: select "id", "name", "email", "createdAt" from "users" -- []
[16:22:03.782] INFO (99349): [db] result: 1 rows
idleTimeoutMillisの設定で一定の時間が経つと接続が破棄されます。
その後にAPI実行して再接続処理が走ると、 3〜4秒 時間がかかりました。
[16:34:53.692] INFO (99349): [db] query: select "id", "name", "email", "createdAt" from "users" -- []
[16:34:54.567] INFO (99349): [getAuthToken] called
[16:34:54.569] INFO (99349): [getAuthToken] cache hit. expires at 2026-03-27T16:35:55
[16:34:57.019] INFO (99349): [pool] new connection established
[16:34:57.076] INFO (99349): [db] result: 1 rows
注意点
Internet Access Gatewayは無効化できない
Express ConfigurationのInternet Access Gatewayは必ず有効になります。インターネットから直接接続できる手軽さが魅力ですが、その分アクセス制御が重要です。本番利用時はIAM認証の設定を適切に行うよう注意してください。
おわりに
Aurora PostgreSQL Serverlessのエクスプレス設定にDrizzle ORMで接続する方法を試してみました。
IAM認証のみという制約はありますが、@aws-sdk/rds-signer と node-postgres ( pg ) を組み合わせることでスムーズに対応できます。キャッシュを加えればAWS APIの呼び出しも最小限に抑えられます。
VPCなしで手軽に使えるサーバーレスPostgreSQLとして、個人開発やプロトタイプ作成に重宝しそうです。
次はAWS Lambdaから使えるかも試してみたいです。
このブログがどなたかのお役に立てれば幸いです。






