Drizzle Client

Database client for Entrolytics with Drizzle ORM

Drizzle Client

The @entro314labs/drizzle-client package provides a type-safe database client for Entrolytics built on Drizzle ORM.

Installation

pnpm add @entro314labs/drizzle-client
npm install @entro314labs/drizzle-client
yarn add @entro314labs/drizzle-client
bun add @entro314labs/drizzle-client

Quick Start

import { createClient } from '@entro314labs/drizzle-client'

const db = createClient({
  connectionString: process.env.DATABASE_URL,
})

// Query websites
const websites = await db.query.website.findMany()

Configuration

import { createClient } from '@entro314labs/drizzle-client'

const db = createClient({
  // Required
  connectionString: process.env.DATABASE_URL,

  // Optional
  replicaConnectionString: process.env.DATABASE_REPLICA_URL,
  type: 'neon', // 'neon' | 'pg'
  maxConnections: 10,
  idleTimeout: 30000,
  connectionTimeout: 10000,
})

Configuration Options

OptionTypeDefaultDescription
connectionStringstring-Primary database connection URL (required)
replicaConnectionStringstring-Read replica connection URL
typestring'neon'Database type: 'neon' or 'pg'
maxConnectionsnumber10Maximum connection pool size
idleTimeoutnumber30000Idle connection timeout (ms)
connectionTimeoutnumber10000Connection timeout (ms)

Database Schema

The client includes the complete Entrolytics schema:

Core Tables

TableDescription
userUser accounts (synced from Clerk)
orgOrganizations
orgUserOrganization membership with roles
websiteTracked websites
sessionVisitor sessions with device/geo data
websiteEventPage views and custom events

Feature Tables

TableDescription
boardCustom dashboards
boardWidgetDashboard widgets
reportSaved reports
segmentUser segments
linkShort links for campaigns
pixelConversion tracking pixels
revenueRevenue tracking

Data Tables

TableDescription
eventDataCustom event properties
sessionDataCustom session properties

API Reference

Client Methods

getReplica()

Get the replica client for read operations (falls back to primary if no replica).

const replica = db.getReplica()
const websites = await replica.query.website.findMany()

getPrimary()

Get the primary client for write operations.

const primary = db.getPrimary()
await primary.insert(website).values({ name: 'My Site', domain: 'example.com' })

execute(sql)

Execute raw SQL queries. Automatically routes SELECT to replica.

const result = await db.execute(sql`SELECT * FROM website WHERE domain = ${domain}`)

transaction(callback, options?)

Execute queries in a transaction.

await db.transaction(async (tx) => {
  await tx.insert(website).values({ name: 'My Site', domain: 'example.com' })
  await tx.insert(orgWebsite).values({ orgId, websiteId })
}, { isolationLevel: 'read committed' })

batch(queries)

Execute multiple queries in parallel.

const [users, websites, orgs] = await db.batch([
  db.query.user.findMany(),
  db.query.website.findMany(),
  db.query.org.findMany(),
])

healthCheck()

Check database connection health.

const { connected, latency } = await db.healthCheck()
console.log(`Database connected: ${connected}, latency: ${latency}ms`)

close()

Close all database connections.

await db.close()

Query Examples

Find Websites

import { eq, and, desc } from '@entro314labs/drizzle-client'

// Find all websites for an org
const websites = await db.query.website.findMany({
  where: eq(website.orgId, orgId),
  orderBy: desc(website.createdAt),
})

// Find website with related data
const site = await db.query.website.findFirst({
  where: eq(website.id, websiteId),
  with: {
    org: true,
  },
})

Insert Data

import { website } from '@entro314labs/drizzle-client'

const newWebsite = await db.insert(website).values({
  name: 'My Website',
  domain: 'example.com',
  orgId: 'org_123',
}).returning()

Update Data

import { eq, website } from '@entro314labs/drizzle-client'

await db.update(website)
  .set({ name: 'Updated Name' })
  .where(eq(website.id, websiteId))

Delete Data

import { eq, website } from '@entro314labs/drizzle-client'

await db.delete(website)
  .where(eq(website.id, websiteId))

Read Replicas

For high-traffic applications, use read replicas:

const db = createClient({
  connectionString: process.env.DATABASE_URL,
  replicaConnectionString: process.env.DATABASE_REPLICA_URL,
})

// Reads automatically go to replica
const websites = await db.query.website.findMany()

// Writes go to primary
await db.insert(website).values({ ... })

Transactions

await db.transaction(async (tx) => {
  // All operations in this callback are atomic
  const org = await tx.insert(org).values({ name: 'New Org' }).returning()

  await tx.insert(orgUser).values({
    orgId: org[0].id,
    userId: currentUserId,
    role: 'org-owner',
  })
})

TypeScript Types

All schema types are exported:

import type {
  User,
  Org,
  OrgUser,
  Website,
  Session,
  WebsiteEvent,
  Board,
  BoardWidget,
  Report,
  Segment,
  Link,
  Pixel,
  Revenue,
} from '@entro314labs/drizzle-client'

Drizzle Operators

Re-exported for convenience:

import {
  eq,
  ne,
  gt,
  gte,
  lt,
  lte,
  like,
  ilike,
  inArray,
  notInArray,
  isNull,
  isNotNull,
  and,
  or,
  not,
  asc,
  desc,
  sql,
} from '@entro314labs/drizzle-client'

Neon Postgres

The client is optimized for Neon serverless Postgres:

const db = createClient({
  connectionString: process.env.NEON_DATABASE_URL,
  type: 'neon',
})

For standard PostgreSQL:

const db = createClient({
  connectionString: process.env.DATABASE_URL,
  type: 'pg',
})

Requirements

  • Node.js >= 22.x
  • PostgreSQL 14+ (or Neon)

License

MIT