ModelQueryBuilder (Type-Safe)
The ModelQueryBuilder
is the primary, partially type-safe query API for Hysteria ORM models. All calls to User.query()
, Post.query()
, etc., return a ModelQueryBuilder instance.
The query builder is partially type safe, triggering intellisense for developer experience but still allowing the developer to write whatever he wants
Key Features
- Fully partially type-safe queries and results
- Decorator and relation support
- Rich API for filtering, selecting, joining, and more
- Supports advanced features like CTEs, annotations, pagination, and soft deletes
Best Practice: Use ModelQueryBuilder for all application logic and model-based queries.
Example Usage
const users = await User.query().where('status', 'active').many();
Filtering
const users = await User.query().where('age', '>', 18).andWhere('isActive', true).many();
Selecting Columns
const names = await User.query().select('name').many();
Pagination
const page = await User.query().paginate(1, 10);
console.log(page.data, page.paginationMetadata);
Joins
const postsWithUsers = await Post.query()
.join('users', 'posts.userId', 'users.id')
.select('posts.*', 'users.name')
.many();
Annotations
const user = await User.query()
.annotate('COUNT(*)', 'count')
.first();
console.log(user.$annotations.count);
CTEs (Common Table Expressions)
const users = await User.query()
.with('active_users', (cte) =>
cte.newCte('users_cte', (b) => b.select('name').where('isActive', true))
)
.many();
Pluck
const names = await User.query().pluck('name'); // string[]
Increment / Decrement
await User.query().increment('age', 1);
await User.query().decrement('age', 1);
Locking
const users = await User.query().lockForUpdate().many();
const users = await User.query().forShare().many();
clearSelect
const users = await User.query().select('name').clearSelect().many();
Soft Delete
await User.query().softDelete({ column: 'deleted_at' });
Type Safety Example
const users = await User.query().where('email', 'like', '%@example.com').many();
// users: User[]
Comparison to QueryBuilder
- Type Safety: ModelQueryBuilder is fully typed, QueryBuilder is not.
- Decorator/Relation Support: Only ModelQueryBuilder supports model decorators and relations.
- Use Case: Use ModelQueryBuilder for all model logic; use QueryBuilder for raw SQL.
Full API Reference
Filtering
where
,orWhere
,andWhere
,whereIn
,whereNotIn
,whereNull
,whereNotNull
,whereBetween
,whereNot
,whereLike
,whereNotLike
,andWhereLike
,andWhereNotLike
,orWhereLike
,orWhereNotLike
,whereExists
,whereNotExists
await User.query().where('email', 'like', '%@example.com').many();
await User.query().whereIn('status', ['active', 'pending']).many();
await User.query().whereNull('deletedAt').many();
await User.query().whereBetween('age', [18, 30]).many();
Subqueries & Nested Conditions
whereSubQuery
,andWhereSubQuery
,orWhereSubQuery
,whereBuilder
,andWhereBuilder
,orWhereBuilder
await User.query().whereSubQuery('id', (sub) => sub.select('userId').from('posts').where('published', true)).many();
await User.query().whereBuilder((qb) => qb.where('age', '>', 18).orWhere('isActive', true)).many();
Joins
join
,leftJoin
,rightJoin
,innerJoin
(by table name or model)
await Post.query().join('users', 'posts.userId', 'users.id').many();
await Post.query().leftJoin(User, 'id', 'userId').many();
Group By & Having
groupBy
,having
await User.query().groupBy('status').having('COUNT(*)', '>', 1).many();
Unions
union
,unionAll
await User.query().select('name').union((qb) => qb.select('authorName')).many();
Aggregates
getCount
,getMax
,getMin
,getAvg
,getSum
const count = await User.query().getCount();
const maxAge = await User.query().getMax('age');
Select & Raw Select
select
,selectRaw
,clearSelect
await User.query().select('name', 'email').many();
await User.query().selectRaw('count(*) as count').first();
await User.query().clearSelect().many();
Annotations
annotate
,removeAnnotations
,clearRemoveAnnotations
await User.query().annotate('COUNT(*)', 'count').first();
await User.query().removeAnnotations().first();
Pluck
pluck
const names = await User.query().pluck('name');
Pagination
paginate
,limit
,offset
const page = await User.query().paginate(1, 10);
await User.query().limit(5).offset(10).many();
Locking
lockForUpdate
,forShare
await User.query().lockForUpdate().many();
await User.query().forShare().many();
CTEs (Common Table Expressions)
with
await User.query().with('normal', (cte) => cte.newCte('users_cte', (b) => b.select('name'))).many();
Relation Loading (ModelQueryBuilder only)
withRelation
,clearRelations
await User.query().withRelation('posts').many();
await User.query().withRelation('addresses', Address, (qb) => qb.withRelation('users')).many();
Copying & Query Output
copy
,toQuery
,unWrap
const qb = User.query().where('isActive', true);
const qbCopy = qb.copy();
const sql = qb.toQuery();
ModelQueryBuilder Only
removeAnnotations
,clearRemoveAnnotations
,withRelation
,clearRelations
Next: QueryBuilder (Raw SQL)