Aurora PostgreSQL Serverless エクスプレス設定に Drizzle ORM で接続してみた

Aurora PostgreSQL Serverless エクスプレス設定に Drizzle ORM で接続してみた

VPC不要・IAM認証のみのAurora PostgreSQL Serverlessエクスプレス設定に、Drizzle ORMで接続する方法を解説。サンプルコード付き。 @aws-sdk/rds-signer でトークンを取得し、node-p ostgresのパスワードにasync関数を渡すことで接続ごとに自動取得できます。
2026.03.27

Aurora PostgreSQL Serverless のエクスプレス設定(以下、Aurora Expressと呼びます)という、VPC不要・数秒でクラスターが作れる新しいAuroraのオプションが登場しました。

弊社ブログでも紹介しているので詳しくはこちらをご覧ください。

https://dev.classmethod.jp/articles/aurora-postgresql-express-configuration/

https://dev.classmethod.jp/articles/aurora-postgresql-express-configuration-serverless-database-creation-in-seconds/

今回は、このAurora ExpressにTypeScript製ORM「Drizzle ORM」を使って接続してみました。IAM認証によるトークンの扱いがポイントになります。

なお、Aurora DSQLでもdrizzleを使った接続記事があります。今回のExpressとはサービスが異なりますのでご注意ください。

最初に結論

  • Aurora Express は IAM認証のみ をサポート
  • Drizzleから接続するには、@aws-sdk/rds-signer でIAM認証トークンを生成してパスワードとして渡す
  • node-postgrespg)の接続設定に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で公開しています。

https://github.com/rednes/aurora-express-drizzle-sample

ディレクトリ構成は以下の通りです。

.
├── 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の認証情報 が必要です。

$ 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-signerSigner クラスで生成できます。

const signer = new Signer({
  hostname: DB_HOST,
  port: 5432,
  username: DB_USER,
  region: AWS_REGION,
});

pg.Poolpassword オプションにはasync関数を渡せます。これを利用して、新しい接続が確立されるたびにトークンを取得する関数を渡します。

const pool = new Pool({
  host: DB_HOST,
  // ...
  ssl: true, // SSLは必須
  password: () => signer.getAuthToken(),
});

ただし、IAM認証トークンは有効期限が 15分 です。接続のたびに毎回AWS APIを叩くのは無駄なので、有効期限内はトークンをキャッシュし、期限の5分前になったタイミングで再取得するようにします。

また、pg.PoolidleTimeoutMillis(アイドル接続を破棄するまでの時間)はデフォルト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-signernode-postgrespg ) を組み合わせることでスムーズに対応できます。キャッシュを加えればAWS APIの呼び出しも最小限に抑えられます。

VPCなしで手軽に使えるサーバーレスPostgreSQLとして、個人開発やプロトタイプ作成に重宝しそうです。
次はAWS Lambdaから使えるかも試してみたいです。

このブログがどなたかのお役に立てれば幸いです。

この記事をシェアする

FacebookHatena blogX

関連記事