Skip to content

Query Builder

Build complex database queries with a fluent, type-safe API.

Basic Usage

typescript
import { User } from './models/User';

// Select all users
const users = await User.query();

// Select with conditions
const activeUsers = await User.query()
  .where('active', true);

// Select specific columns
const users = await User.query()
  .select('id', 'name', 'email');

WHERE Clauses

Basic WHERE

typescript
// Simple equality
await User.query().where('name', 'John');

// With operator
await User.query().where('age', '>', 18);

// Multiple conditions (AND)
await User.query()
  .where('active', true)
  .where('age', '>=', 18);

OR WHERE

typescript
await User.query()
  .where('role', 'admin')
  .orWhere('role', 'moderator');

WHERE IN

typescript
await User.query()
  .whereIn('status', ['active', 'pending']);

await User.query()
  .whereNotIn('role', ['guest', 'banned']);

WHERE NULL

typescript
await User.query().whereNull('deletedAt');
await User.query().whereNotNull('emailVerifiedAt');

WHERE BETWEEN

typescript
await User.query()
  .whereBetween('age', [18, 65]);

Raw WHERE

typescript
await User.query()
  .whereRaw('LOWER(email) = ?', ['john@example.com']);

Ordering

typescript
// Order by single column
await User.query().orderBy('createdAt', 'desc');

// Order by multiple columns
await User.query()
  .orderBy('role', 'asc')
  .orderBy('name', 'asc');

// Raw order
await User.query()
  .orderByRaw('RAND()');

Limiting & Pagination

typescript
// Limit results
await User.query().limit(10);

// Offset
await User.query().offset(20).limit(10);

// Pagination helper
await User.query().paginate(page: 2, perPage: 20);

Aggregates

typescript
// Count
const count = await User.query().count();

// Count with condition
const activeCount = await User.query()
  .where('active', true)
  .count();

// Sum
const totalAge = await User.query().sum('age');

// Average
const avgAge = await User.query().avg('age');

// Min/Max
const youngest = await User.query().min('age');
const oldest = await User.query().max('age');

Joins

typescript
// Inner join
await User.query()
  .join('posts', 'users.id', 'posts.userId')
  .select('users.*', 'posts.title');

// Left join
await User.query()
  .leftJoin('posts', 'users.id', 'posts.userId');

// Multiple joins
await User.query()
  .join('posts', 'users.id', 'posts.userId')
  .join('comments', 'posts.id', 'comments.postId');

Grouping

typescript
await User.query()
  .select('role', db.raw('COUNT(*) as count'))
  .groupBy('role');

await User.query()
  .groupBy('role')
  .having('count', '>', 5);

Subqueries

typescript
const subquery = Post.query()
  .select('userId')
  .where('published', true);

await User.query()
  .whereIn('id', subquery);

Raw Queries

typescript
import { db } from '@curisjs/db';

// Raw select
const users = await db.raw('SELECT * FROM users WHERE active = ?', [true]);

// Raw with query builder
await User.query()
  .whereRaw('YEAR(createdAt) = ?', [2024]);

Complete Example

typescript
const results = await User.query()
  .select('id', 'name', 'email', 'role')
  .where('active', true)
  .where('age', '>=', 18)
  .whereIn('role', ['user', 'admin'])
  .whereNotNull('emailVerifiedAt')
  .orderBy('createdAt', 'desc')
  .limit(20)
  .offset(0);

For more examples, see the Models documentation.

Released under the MIT License.