https://github.com/stacksjs/bun-query-builder
A safe, performant & fully-typed query builder for Bun.
https://github.com/stacksjs/bun-query-builder
bun javascript mysql postgres query-builder sqlite typescript
Last synced: 19 days ago
JSON representation
A safe, performant & fully-typed query builder for Bun.
- Host: GitHub
- URL: https://github.com/stacksjs/bun-query-builder
- Owner: stacksjs
- License: mit
- Created: 2025-08-11T16:42:46.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2026-04-11T08:19:26.000Z (20 days ago)
- Last Synced: 2026-04-11T08:23:31.486Z (20 days ago)
- Topics: bun, javascript, mysql, postgres, query-builder, sqlite, typescript
- Language: TypeScript
- Homepage: https://bun-query-builder.netlify.app
- Size: 32.2 MB
- Stars: 9
- Watchers: 0
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: .github/CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
- Code of conduct: .github/CODE_OF_CONDUCT.md
- Security: .github/SECURITY.md
Awesome Lists containing this project
README

[![npm version][npm-version-src]][npm-version-href]
[![GitHub Actions][github-actions-src]][github-actions-href]
[](http://commitizen.github.io/cz-cli/)
# bun-query-builder
Fully-typed, model-driven Query Builder for Bun’s native `sql`.
Define your data model once and get a type-safe query experience _(a la Kysely/Laravel)_, powered by Bun’s tagged templates for safety and performance.
## Features
### Core Query Building
- **Typed from Models**: Infer tables/columns/PKs from your model files; `selectFrom('users')` and `where({ active: true })` are typed.
- **Fluent Builder**: `select/insert/update/delete`, `where/andWhere/orWhere`, `join/leftJoin/rightJoin/crossJoin`, `groupBy/having`, `union/unionAll`.
- **Aggregations**: `count()`, `avg()`, `sum()`, `max()`, `min()` with full type safety.
- **Batch Operations**: `insertMany()`, `updateMany()`, `deleteMany()` for efficient bulk operations.
### Advanced Features
- **Relations**: `with(...)`, `withCount(...)`, `whereHas(...)`, `has()`, `doesntHave()`, `selectAllRelations()` with configurable aliasing and constraint callbacks.
- **Query Scopes**: Define reusable query constraints on models for cleaner, more maintainable code.
- **Query Caching**: Built-in LRU cache with TTL support via `cache(ttlMs)`, `clearQueryCache()`, `setQueryCacheMaxSize()`.
- **Model Hooks**: Lifecycle events - `beforeCreate`, `afterCreate`, `beforeUpdate`, `afterUpdate`, `beforeDelete`, `afterDelete`.
### Utilities & Helpers
- **Utilities**: `distinct/distinctOn`, `orderByDesc/latest/oldest/inRandomOrder`, `whereColumn/whereRaw/groupByRaw/havingRaw`, JSON/date helpers.
- **Pagination**: `paginate`, `simplePaginate`, `cursorPaginate`, plus `chunk/chunkById/eachById`.
- **Soft Deletes**: `withTrashed()`, `onlyTrashed()` for logical deletion support.
### Database Operations
- **Transactions**: `transaction` with retries/backoff/isolation/onRetry/afterCommit; `savepoint`; distributed tx helpers.
- **Migrations**: Generate and execute migrations from models with full diff support.
- **Seeders**: Database seeding with fake data generation via `ts-mocker` (faker alternative).
- **Raw Queries**: Tagged templates and parameterized queries with `raw()` and `unsafe()`.
### Configuration & Integration
- **Configurable**: Dialect hints, timestamps, alias strategies, relation FK formats, JSON mode, random function, shared lock syntax.
- **Bun API passthroughs**: `unsafe`, `file`, `simple`, pool `reserve/release`, `close`, `ping/waitForReady`.
- **CLI**: Introspection, query printing, connectivity checks, file/unsafe execution, explain.
> Note: LISTEN/NOTIFY and COPY helpers are scaffolded and will be wired as Bun exposes native APIs.
## Get Started
### Installation
```bash
bun add bun-query-builder
```
### Usage
```ts
import { buildDatabaseSchema, buildSchemaMeta, createQueryBuilder } from 'bun-query-builder'
// Load or define your model files (see docs for model shape)
const models = {
User: { name: 'User', table: 'users', primaryKey: 'id', attributes: { id: { validation: { rule: {} } }, name: { validation: { rule: {} } }, active: { validation: { rule: {} } } } },
} as const
const schema = buildDatabaseSchema(models as any)
const meta = buildSchemaMeta(models as any)
const db = createQueryBuilder({ schema, meta })
// Fully-typed query
const q = db
.selectFrom('users')
.where({ active: true })
.orderBy('created_at', 'desc')
.limit(10)
const rows = await q.execute()
```
### Aggregations
```ts
// Get average age of active users
const avgAge = await db.selectFrom('users')
.where({ active: true })
.avg('age')
// Count total posts
const totalPosts = await db.selectFrom('posts').count()
// Get max and min scores
const maxScore = await db.selectFrom('users').max('score')
const minScore = await db.selectFrom('users').min('score')
```
### Batch Operations
```ts
// Insert multiple records at once
await db.insertMany('users', [
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Charlie', email: 'charlie@example.com' },
])
// Update multiple records matching conditions
await db.updateMany('users', { verified: false }, { status: 'pending' })
// Delete multiple records by IDs
await db.deleteMany('users', [1, 2, 3, 4, 5])
```
### Query Caching
```ts
// Cache query results for 60 seconds (default)
const users = await db.selectFrom('users')
.where({ active: true })
.cache()
.get()
// Custom cache TTL (5 seconds)
const posts = await db.selectFrom('posts')
.orderBy('created_at', 'desc')
.limit(10)
.cache(5000)
.get()
// Clear all cached queries
clearQueryCache()
// Configure cache size
setQueryCacheMaxSize(500)
```
### Model Hooks
```ts
const db = createQueryBuilder({
schema,
meta,
hooks: {
beforeCreate: async ({ table, data }) => {
console.log(`Creating ${table}:`, data)
// Modify data, validate, or throw to prevent creation
},
afterCreate: async ({ table, data, result }) => {
console.log(`Created ${table}:`, result)
// Trigger notifications, update caches, etc.
},
beforeUpdate: async ({ table, data, where }) => {
// Audit logging, validation, etc.
},
afterUpdate: async ({ table, data, where, result }) => {
// Clear related caches, send webhooks, etc.
},
beforeDelete: async ({ table, where }) => {
// Prevent deletion, check constraints, etc.
},
afterDelete: async ({ table, where, result }) => {
// Clean up related data, update aggregates, etc.
},
}
})
```
### Query Scopes
```ts
// Define scopes on your models
const User = {
name: 'User',
table: 'users',
scopes: {
active: (qb) => qb.where({ status: 'active' }),
verified: (qb) => qb.where({ email_verified_at: ['IS NOT', null] }),
premium: (qb) => qb.where({ subscription: 'premium' }),
},
// ... other model properties
}
// Use scopes in queries
const activeUsers = await db.selectFrom('users')
.scope('active')
.scope('verified')
.get()
```
### Relations with Constraints
```ts
// Eager load with constraints
const users = await db.selectFrom('users')
.with({
posts: (qb) => qb.where('published', '=', true).orderBy('created_at', 'desc')
})
.get()
// Check for related records
const usersWithPosts = await db.selectFrom('users')
.has('posts')
.get()
// Query by relationship existence
const activeAuthors = await db.selectFrom('users')
.whereHas('posts', (qb) => qb.where('published', '=', true))
.get()
```
## Migrations
Generate and execute migrations from your models:
```ts
import { generateMigration, executeMigration } from 'bun-query-builder'
// Generate migration from models directory
const migration = await generateMigration('./models', {
dialect: 'postgres',
apply: true,
full: true
})
// Execute the migration
await executeMigration(migration)
```
## Database Seeding
Populate your database with test data using seeders powered by [ts-mocker](https://github.com/stacksjs/ts-mocker):
### Creating a Seeder
```bash
# Generate a new seeder
bun qb make:seeder User
# This creates database/seeders/UserSeeder.ts
```
### Writing a Seeder
```ts
import { Seeder } from 'bun-query-builder'
import { faker } from 'ts-mocker'
export default class UserSeeder extends Seeder {
async run(qb: any): Promise {
// Generate 50 users with realistic fake data
const users = Array.from({ length: 50 }, () => ({
name: faker.person.fullName(),
email: faker.internet.email(),
age: faker.number.int(18, 80),
role: faker.helpers.arrayElement(['admin', 'user', 'moderator']),
created_at: new Date(),
updated_at: new Date(),
}))
await qb.table('users').insert(users).execute()
}
// Control execution order (lower runs first)
get order(): number {
return 10 // Default is 100
}
}
```
### Running Seeders
```bash
# Run all seeders
bun qb seed
bun qb db:seed
# Run a specific seeder
bun qb seed --class UserSeeder
# Drop all tables, re-run migrations and seed
bun qb db:fresh
```
### Programmatic Usage
```ts
import { runSeeders, runSeeder } from 'bun-query-builder'
// Run all seeders
await runSeeders({
seedersDir: './database/seeders',
verbose: true
})
// Run specific seeder
await runSeeder('UserSeeder', { verbose: true })
```
### CLI
```bash
# Model Generation
query-builder make:model User
query-builder make:model Post --table=blog_posts
query-builder model:show User # Show detailed model info
# Schema Introspection
query-builder introspect ./app/Models --verbose
query-builder sql ./app/Models users --limit 5
# Migrations
query-builder migrate ./app/Models --dialect postgres
query-builder migrate:generate # Generate migration from drift
query-builder migrate:status # Show migration status
query-builder migrate:list # List all migrations
query-builder migrate:rollback --steps 2 # Rollback migrations
query-builder migrate:fresh ./app/Models
query-builder reset ./app/Models
# Database Info
query-builder db:info # Show database statistics
query-builder db:stats # Alias for db:info
query-builder inspect users # Inspect table structure
query-builder table:info users # Alias for inspect
query-builder db:wipe --force # Drop all tables
query-builder db:optimize # Optimize database (VACUUM, ANALYZE)
query-builder db:optimize --aggressive # Aggressive optimization
# Interactive Console
query-builder console # Start REPL
query-builder tinker # Alias for console
# Seeders
query-builder make:seeder User
query-builder seed
query-builder db:seed --class UserSeeder
query-builder db:fresh
# Data Management
query-builder export users --format json
query-builder export users --format csv --output users.csv
query-builder import users users.json --truncate
query-builder dump --tables users,posts
# Cache Management
query-builder cache:clear
query-builder cache:stats
query-builder cache:config --size 500
# Performance
query-builder benchmark --iterations 1000
query-builder benchmark --operations select,insert
query-builder query:explain-all ./queries # Batch EXPLAIN analysis
# Schema Validation
query-builder validate:schema ./app/Models
query-builder check # Alias for validate:schema
# Connectivity
query-builder ping
query-builder wait-ready --attempts 30 --delay 250
# Execute SQL
query-builder file ./migrations/seed.sql
query-builder unsafe "SELECT * FROM users WHERE id = $1" --params "[1]"
query-builder explain "SELECT * FROM users WHERE active = true"
# Diagrams & Visualization
query-builder relation:diagram # Generate Mermaid ER diagram
query-builder relation:diagram --format dot # Generate Graphviz DOT
query-builder relation:diagram --output schema.mmd
```
## Performance
Benchmarked on Apple M3 Pro, Bun 1.3.11, SQLite with 1,000 users and 5,000 posts. Kysely uses the official [`kysely-bun-sqlite`](https://github.com/nicksrandall/kysely-bun-sqlite) adapter.
### Basic Queries
| Benchmark | bun-query-builder | Kysely | Drizzle | Prisma |
|-----------|------------------:|-------:|--------:|-------:|
| SELECT: Find by ID | **8.3 µs** | 15.4 µs | 42.1 µs | 86.0 µs |
| SELECT: Active users | **221 µs** | 233 µs | 466 µs | 3,040 µs |
| SELECT: With LIMIT | **9.2 µs** | 19.3 µs | 41.2 µs | 106 µs |
| SELECT: COUNT | **6.8 µs** | 30.0 µs | 32.6 µs | 81.4 µs |
| INSERT: Single | **439 µs** | 558 µs | 472 µs | 657 µs |
| UPDATE: Single | **8.3 µs** | 13.4 µs | 22.8 µs | 123 µs |
| DELETE: Single | **7.5 µs** | 11.9 µs | 16.7 µs | 55 µs |
### Advanced Queries
| Benchmark | bun-query-builder | Kysely | Drizzle | Prisma |
|-----------|------------------:|-------:|--------:|-------:|
| JOIN: Users with posts | **28.1 µs** | 44.3 µs | 83.2 µs | 1,560 µs |
| AGGREGATE: Average age | **29.3 µs** | 39.8 µs | 39.2 µs | 91.8 µs |
| WHERE: Complex conditions | **98.7 µs** | 110 µs | 212 µs | 1,060 µs |
| ORDER BY + LIMIT | **264 µs** | 313 µs | 337 µs | 511 µs |
| GROUP BY + HAVING | **616 µs** | 625 µs | 779 µs | 1,740 µs |
### Batch Operations
| Benchmark | bun-query-builder | Kysely | Drizzle | Prisma |
|-----------|------------------:|-------:|--------:|-------:|
| INSERT MANY: 100 users | **704 µs** | 1,080 µs | 1,380 µs | 1,410 µs |
| UPDATE MANY | **11.0 ms** | 10.8 ms | 10.7 ms | 11.9 ms |
| DELETE MANY: By IDs | **15.5 µs** | 22.4 µs | 33.8 µs | 69.0 µs |
| SELECT: 1000 rows | **248 µs** | 271 µs | 562 µs | 3,440 µs |
Lowest time per benchmark is **bolded**. bun-query-builder wins 16 of 16 benchmarks. [Full benchmark details →](./packages/benchmark/README.md)
## Testing
```bash
bun test
```
## Changelog
Please see our [releases](https://github.com/stackjs/bun-query-builder/releases) page for more information on what has changed recently.
## Contributing
Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.
## Community
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
[Discussions on GitHub](https://github.com/stacksjs/ts-starter/discussions)
For casual chit-chat with others using this package:
[Join the Stacks Discord Server](https://discord.gg/stacksjs)
## Postcardware
“Software that is free, but hopes for a postcard.” We love receiving postcards from around the world showing where Stacks is being used! We showcase them on our website too.
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States 🌎
## Sponsors
We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
- [JetBrains](https://www.jetbrains.com/)
- [The Solana Foundation](https://solana.com/)
## License
The MIT License (MIT). Please see [LICENSE](LICENSE.md) for more information.
Made with 💙
[npm-version-src]: https://img.shields.io/npm/v/bun-query-builder?style=flat-square
[npm-version-href]: https://npmjs.com/package/bun-query-builder
[github-actions-src]: https://img.shields.io/github/actions/workflow/status/stacksjs/ts-starter/ci.yml?style=flat-square&branch=main
[github-actions-href]: https://github.com/stacksjs/ts-starter/actions?query=workflow%3Aci