@relq/orm
v0.1.3
Published
The runtime engine for Relq — type-safe PostgreSQL ORM for TypeScript
Maintainers
Readme
Install
npm install @relq/orm
# Schema definition & CLI (dev dependency)
npm install -D @relq/kitQuick Start
import { Relq } from '@relq/orm'
import { schema } from './db/schema'
const db = new Relq(schema, 'postgres', {
host: 'localhost',
database: 'myapp',
user: 'postgres',
password: 'password',
})
await db.initialize()Queries
Every query is fully typed. Column names, return types, and conditions are all inferred from your schema.
Select
// All columns
const users = await db.table.users.select().all()
// Specific columns — return type narrows automatically
const names = await db.table.users
.select('id', 'name', 'email')
.where(q => q.equal('status', 'active'))
.orderBy('createdAt', 'DESC')
.limit(10)
.all()
// Single row
const user = await db.table.users
.select()
.where(q => q.equal('id', userId))
.get()Insert
// Single row
await db.table.users
.insert({ name: 'Alice', email: '[email protected]' })
.returning('*')
.run()
// Bulk insert with upsert
await db.table.users
.insert(batchOfUsers)
.onConflict('email')
.doUpdate({ name: 'excluded.name', updatedAt: PG.now() })
.run()Update
await db.table.users
.update({ status: 'inactive', updatedAt: PG.now() })
.where(q => q.equal('id', userId))
.returning('id', 'status')
.run()Delete
await db.table.users
.delete()
.where(q => q.equal('id', userId))
.returning('*')
.run()Count
const total = await db.table.users.count().get()
const grouped = await db.table.users
.count()
.group('active', q => q.equal('status', 'active'))
.group('inactive', q => q.equal('status', 'inactive'))
.get()
// { active: 142, inactive: 37 }Joins
Type-safe joins with automatic foreign key detection.
const orders = await db.table.orders
.select('id', 'total')
.join('users')
.select('name', 'email')
.where(q => q.gte('total', 100))
.all()
// Array<{ id, total, users: { name, email } }>Computed Columns & Aggregates
const report = await db.table.users
.select('id', 'name')
.include((a, t) => ({
orderCount: a.count(t.orders.id),
totalSpent: a.sum(t.orders.amount),
}))
.all()
// Array<{ id, name, orderCount: number, totalSpent: number }>Transactions
const result = await db.transaction(async tx => {
const user = await tx.table.users
.insert({ name: 'Bob', email: '[email protected]' })
.returning('*')
.run()
await tx.table.logs
.insert({ userId: user.id, action: 'signup' })
.run()
return user
})Pagination
const page = await db.table.posts
.select()
.orderBy('createdAt', 'DESC')
.pagination({ page: 2, perPage: 20 })
// page.data — the rows
// page.pagination.total — total count
// page.pagination.totalPages
// page.pagination.hasNext / hasPrevCursor Iteration
Process large datasets without loading everything into memory.
await db.table.events
.select()
.where(q => q.gte('createdAt', startDate))
.each(async (event, index) => {
await processEvent(event)
})Pub/Sub
Real-time notifications over PostgreSQL LISTEN/NOTIFY.
await db.subscribe<{ userId: string }>('user_events', payload => {
console.log('New event for', payload.userId)
})Raw SQL
Escape hatch when you need it.
const results = await db.raw('SELECT * FROM users WHERE id = $1', userId)Supported Dialects
| Dialect | Status | |---------|--------| | PostgreSQL | Stable | | Nile (multi-tenant) | Stable | | CockroachDB | Stable | | AWS Aurora DSQL | Stable | | SQLite | Coming soon | | Turso (libSQL) | Coming soon | | MySQL | Coming soon | | PlanetScale | Coming soon |
Ecosystem
| Package | Purpose | |---------|---------| | @relq/orm | Runtime — queries, mutations, transactions | | @relq/kit | Tooling — schema definition, CLI, migrations | | @relq/zod | Validation — Zod schema generation (coming soon) |
