0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Neon(Postgres)のDockerをローカルで稼働させ、Drizzleを使ったマイグレーションとCloudflare Workersのローカル実行からの接続を行ったメモ

Last updated at Posted at 2025-01-05

概要

前回はFirebase Authのローカル化を行った。
今回はNeonDBをローカルで動作させた。

ソースコード

ローカル環境

  • Windows 11 Home 24H2 26100.2605
  • Docker version 27.3.1, build ce12230
  • Node v22.11.0
  • bun 1.1.42
  • ⛅️ wrangler 3.99.0

DBの準備

公式がローカル用の設定を明記している。

こちらにならってdocker-composeを記載。

infra/local/neon/docker-compose.yml
services:
  postgres:
    image: postgres:17
    command: '-d 1'
    volumes:
      - db_data:/var/lib/postgresql/data
    ports:
      - '5432:5432'
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=main
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U postgres']
      interval: 10s
      timeout: 5s
      retries: 5
  neon-proxy:
    image: ghcr.io/timowilhelm/local-neon-http-proxy:main
    environment:
      - PG_CONNECTION_STRING=postgres://postgres:postgres@postgres:5432/main
    ports:
      - '4444:4444'
    depends_on:
      postgres:
        condition: service_healthy
volumes:
  db_data:

docker-compose upで無事起動した。

Drizzleを使ったマイグレーション

ローカルのneonDBへの接続のためにpostgresqlが必要なので追加する。

bun add -D postgres 

ローカル用と本番用と2つの環境ができたので、環境変数も二つ用意。

packages/database/.env.local
NEON_CONNECTION_STRING=postgres://postgres:postgres@127.0.0.1:5432/main
packages/database/.env.prod
NEON_CONNECTION_STRING=postgresql://<DB_USER_NAME>:<DB_PASSWORD>@<NEON_DB_DOMAIN>.us-west-2.aws.neon.tech/main?sslmode=require
packages/database/drizzle.config.ts
import { config } from 'dotenv';
import { defineConfig } from 'drizzle-kit';

- config({ path: '.env.local' });
+ config({ path: process.env.DOTENV_CONFIG_PATH });

export default defineConfig({
  schema: './src/schema.ts',
  out: './migrations',
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.NEON_CONNECTION_STRING!,
  },
});
packages/database/package.json
{
  "name": "@odyssage/database",
  "version": "0.0.0",
  "type": "module",
  "private": true,
  "scripts": {
    "apply": "drizzle-kit generate",
-    "migrate": "drizzle-kit migrate",
+    "migrate": "cross-env DOTENV_CONFIG_PATH=.env.prod drizzle-kit migrate",
+    "migrate-local": "cross-env DOTENV_CONFIG_PATH=.env.local drizzle-kit migrate"
  },
  "devDependencies": {
+    "cross-env": "^7.0.3",
    "dotenv": "^16.4.7",
    "drizzle-kit": "^0.30.1",
+    "postgres": "^3.4.5"
  },
  "dependencies": {
    "@neondatabase/serverless": "^0.10.4",
    "drizzle-orm": "^0.38.3"
  }
}

bun run migrate-localを行いローカルのDBにマイグレーションを行う。

image.png

なお、このときpostgresを追加せずにmigrationのコマンドを実行すると下記のようなエラーとなる。

$ drizzle-kit migrate
No config path provided, using default 'drizzle.config.ts'
Reading config file 'D:\projects\odyssage\packages\database\drizzle.config.ts'
Using '@neondatabase/serverless' driver for database querying
 Warning  '@neondatabase/serverless' can only connect to remote Neon/Vercel Postgres/Supabase instances through a websocket
[⣷] applying migrations...ErrorEvent {
  [Symbol(kTarget)]: _WebSocket {
    _events: [Object: null prototype] {
...
省略
...
  [Symbol(kError)]: Error: Client network socket disconnected before secure TLS connection was established
      at TLSSocket.onConnectEnd (node:_tls_wrap:1732:19)
      at TLSSocket.emit (node:events:530:35)
      at endReadableNT (node:internal/streams/readable:1698:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
    code: 'ECONNRESET',
    path: undefined,
    host: '127.0.0.1',
    port: 443,
    localAddress: undefined
  },
  [Symbol(kMessage)]: 'Client network socket disconnected before secure TLS connection was established'
}
error: script "migrate" exited with code 1

マイグレーション結果の確認

a5SQLを使って確認した。

接続設定。
image.png

マイグレーションしたスキーマが作成されていることを確認。
image.png

バックエンドからの接続

hostsの修正

接続文字列に127.0.0.1を使うと下記エラーが発生する。そのため、127.0.0.1 db.localtest.meの名前解決をローカル環境で行う必要がある。

[wrangler:inf] GET /api/user/ 500 Internal Server Error (26ms)    
X [ERROR] NeonDbError: invalid hostname: Common name inferred from SNI ('0.0.1') is not known

各OSごとの設定を参考に設定した。
C:\Windows\System32\drivers\etc\hostをメモ帳の管理者モードで開いて、下記を追記。

127.0.0.1 db.localtest.me

image.png
image.png
image.png

バックエンドソースの修正

apps/backend/.dev.vars
- NEON_CONNECTION_STRING=postgresql://<DB_USER_NAME>:<DB_PASSWORD>@<NEON_DB_DOMAIN>.us-west-2.aws.neon.tech/main?sslmode=require
+ NEON_CONNECTION_STRING=postgres://postgres:postgres@db.localtest.me:5432/main
packages/database/src/db.ts
- import { neon } from '@neondatabase/serverless';
+ import { neon, neonConfig } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-http';
import type { NeonQueryFunction } from '@neondatabase/serverless';
import type { NeonHttpDatabase } from 'drizzle-orm/neon-http';

type NeonDBClient = NeonHttpDatabase<Record<string, never>> & {
  $client: NeonQueryFunction<false, false>;
};

let db: NeonDBClient | null = null;

export const getDb = (
  connectionString = process.env.NEON_CONNECTION_STRING!,
) => {
  if (db != null) {
    return db;
  }
+  if ( connectionString === 'postgres://postgres:postgres@db.localtest.me:5432/main' ) {
+    neonConfig.fetchEndpoint = `http://db.localtest.me:4444/sql`;
+    neonConfig.useSecureWebSocket = false;
+  }
  const sql = neon(connectionString);
  db = drizzle({ client: sql });
  return db;
};

ちなみに、接続文字列だけ変更すると下記エラーが発生する。

X [ERROR] NeonDbError: Error connecting to database: Network connection lost.

      at execute
  (file:///D:/projects/odyssage/node_modules/@neondatabase/serverless/index.mjs:1549:24)

参考

Local Development with Neon
Neon (Postgres) をコンテナで動かす
Neon Serverless Postgres: 便利な機能と導入パターン
サーバレスDB(Postgresql : NeonDB ) の無料版にCloudflare Workers ( Node.js) から繋いだメモ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?