@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
| Export | Description |
|---|---|
createDb(options?) | Create or return the cached database client singleton |
closeDb() | Gracefully close the database connection |
schema | Re-exported Drizzle schema definitions |
checkDatabaseHealth(db) | Run a health check query and return latency |
and, count, desc, eq, gte, sql, sum | Re-exported Drizzle operators |
Type Exports
| Type | Description |
|---|---|
DatabaseClient | The Drizzle database client type |
DatabaseClientWithConnection | Client paired with the raw connection for graceful shutdown |
DatabaseConfig | Configuration options for createDb |
DatabaseConnection | The underlying PostgreSQL connection type |
DatabaseHealthCheck | Health 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:
| Variable | Default | Description |
|---|---|---|
DATABASE_URL | (required) | PostgreSQL connection string |
DATABASE_POOL_MAX | 10 | Maximum pool size |
DATABASE_IDLE_TIMEOUT | 20 | Idle timeout in seconds |
| (connect timeout) | 10 | Connection 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,
}),
})