Raypx

@raypx/database

Drizzle ORM integration with PostgreSQL connection management.

@raypx/database wraps Drizzle ORM with connection pooling, health checking, and a singleton pattern for PostgreSQL. It is the single source of truth for database access across all feature packages.

Exports

ExportDescription
createDb(options?)Create or return the cached database client singleton
closeDb()Gracefully close the database connection
schemaRe-exported Drizzle schema definitions
checkDatabaseHealth(db)Run a health check query and return latency
and, count, desc, eq, gte, sql, sumRe-exported Drizzle operators

Type Exports

TypeDescription
DatabaseClientThe Drizzle database client type
DatabaseClientWithConnectionClient paired with the raw connection for graceful shutdown
DatabaseConfigConfiguration options for createDb
DatabaseConnectionThe underlying PostgreSQL connection type
DatabaseHealthCheckHealth check result type

Singleton Pattern

createDb() caches the connection on first call. Subsequent calls return the same instance without creating new connections:

import { createDb, closeDb } from "@raypx/database"

const db = createDb() // creates connection
const db2 = createDb() // returns cached instance (db === db2)

// Graceful shutdown
await closeDb()

Configuration

Connection settings come from environment variables validated by @raypx/env:

VariableDefaultDescription
DATABASE_URL(required)PostgreSQL connection string
DATABASE_POOL_MAX10Maximum pool size
DATABASE_IDLE_TIMEOUT20Idle timeout in seconds
(connect timeout)10Connection timeout in seconds

You can also pass options directly:

const db = createDb({
  databaseUrl: "postgres://user:pass@host:5432/db",
  postgresOptions: { max: 5 },
  closeTimeout: 5000,
})

Pool Sizing Guide

Recommended pool sizes based on deployment scale:

  • Single instance: 10-20 connections (default is 10)
  • Multi-instance (2-5 pods): 5-10 connections each
  • Multi-instance (5+ pods): Use PgBouncer for external connection pooling

Health Check

Use checkDatabaseHealth to verify database connectivity:

import { createDb, checkDatabaseHealth } from "@raypx/database"

const db = createDb()
const health = await checkDatabaseHealth(db)

if (!health.healthy) {
  console.error(`Database unhealthy: ${health.error}`)
}
// { healthy: true, latency: 12 }

The health check is also wired into the RPC appRouter.healthCheck endpoint, which returns database and storage health together.

Drizzle Operators

Common Drizzle operators are re-exported for convenience so consumers do not need to import from drizzle-orm directly:

import { eq, and, count, sql } from "@raypx/database"

const users = await db
  .select({ count: count() })
  .from(schema.user)
  .where(eq(schema.user.email, "user@example.com"))

Schema

The schema namespace re-exports all table definitions used across the project. This is passed directly to Better Auth's Drizzle adapter:

import { betterAuth } from "better-auth"
import { drizzleAdapter } from "better-auth/adapters/drizzle"
import { createDb, schema } from "@raypx/database"

betterAuth({
  database: drizzleAdapter(createDb(), {
    provider: "pg",
    schema,
  }),
})

On this page