A flexible and type-safe query builder for constructing complex conditional expressions with support for nested groups, various operators, and function calls.
npm install @andreasnicolaou/query-builderbash
npm
npm install @andreasnicolaou/query-builder
yarn
yarn add @andreasnicolaou/query-builder
pnpm
pnpm add @andreasnicolaou/query-builder
`
$3
For direct browser usage without a build step:
`html
`
$3
#### ES Modules (Recommended)
`typescript
import { QueryBuilder } from '@andreasnicolaou/query-builder';
const qb = new QueryBuilder();
qb.where('name', 'ilike', '%andreas%');
console.log(qb.toString());
`
#### CommonJS
`javascript
const { QueryBuilder } = require('@andreasnicolaou/query-builder');
const qb = new QueryBuilder();
qb.where('age', '>', 30);
console.log(qb.toString());
`
#### Browser (UMD via CDN)
`html
`
#### Browser (ES Modules via CDN)
`html
`
#### TypeScript
`typescript
// Full type safety and IntelliSense support
import { QueryBuilder, QueryBuilderSerialized } from '@andreasnicolaou/query-builder';
const qb: QueryBuilder = new QueryBuilder();
// ...
`
Basic Usage
`typescript
import { QueryBuilder } from '@andreasnicolaou/query-builder';
const query = new QueryBuilder()
.where('name', 'ilike', '%andreas%') // Case-insensitive search
.where('age', 'between', [18, 65], 'and') // Age range
.where('email', 'is not null', undefined, 'and') // Email required
.group((qb) => {
qb.where('status', 'in', ['active', 'pending']).where('created', '>', new Date('2025-01-01').toISOString());
})
.toString();
console.log(query);
// name ilike '%andreas%' and age between 18 and 65 and email is not null and (status in ('active', 'pending') and created > '2025-01-01T00:00:00.000Z')
`
Skipping Empty or Invalid Values
`typescript
import { QueryBuilder } from '@andreasnicolaou/query-builder';
const query = new QueryBuilder()
.skipWhen({ emptyString: true, emptyArray: true })
.where('name', '===', '')
.where('age', '>', 36)
.where('tags', 'in', [])
.toString();
console.log(query); // age > 36
`
UI-Focused Examples
$3
`typescript
// Perfect for building complex search UIs
const searchQuery = new QueryBuilder()
.where('title', 'ilike', '%react%') // Case-insensitive title search
.where('price', 'between', [10, 100], 'and') // Price range
.where('category', 'in', ['books', 'electronics'], 'and') // Multiple categories
.group((nested) => {
nested
.where('rating', '>=', 4) // High rated
.where('featured', '=', true, 'or'); // OR featured items
}, 'and');
// Result: title ilike '%react%' and price between 10 and 100 and category in ('books', 'electronics') and (rating >= 4 or featured = true)
`
$3
`typescript
// Handle optional and required fields elegantly
const userQuery = new QueryBuilder()
.skipWhen({ emptyString: true, null: false }) // Allow explicit nulls
.where('email', 'is not null') // Email required
.where('firstName', 'like', 'John%', 'and') // Starts with John
.where('lastName', 'is not empty', undefined, 'and') // Last name provided
.where('age', 'between', [18, 99], 'and') // Adult users
.where('deletedAt', 'is null', undefined, 'and'); // Active users only
// Result: email is not null and firstName like 'John%' and lastName is not empty and age between 18 and 99 and deletedAt is null
`
UI Validation & Error Handling
`typescript
import { QueryBuilder } from '@andreasnicolaou/query-builder';
// Validate user input before building queries
function buildSearchQuery(formData: any) {
const errors: string[] = [];
// Validate age range
if (formData.ageMin && formData.ageMax) {
const validation = QueryBuilder.validateOperator('between', [formData.ageMin, formData.ageMax]);
if (!validation.valid) {
errors.push(Age range: ${validation.error});
}
}
// Validate category selection
if (formData.categories && formData.categories.length > 0) {
const validation = QueryBuilder.validateOperator('in', formData.categories);
if (!validation.valid) {
errors.push(Categories: ${validation.error});
}
}
if (errors.length > 0) {
throw new Error(Validation errors: ${errors.join(', ')});
}
// Build query after validation
return new QueryBuilder()
.skipWhen({ emptyString: true, emptyArray: true })
.where('name', 'ilike', %${formData.search}%)
.where('age', 'between', [formData.ageMin, formData.ageMax])
.where('category', 'in', formData.categories);
}
`
API
$3
| Method | Description |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| .where(field, operator, value?, logicalOperator?) | Add a condition with any operator |
| .group(callback, logicalOperator?) | Create nested conditions (groups) |
| .skipWhen(options?) | Configure automatic value skipping (null, undefined, '', [], NaN , {} are skipped by default) |
| .toJSON() | Get serializable representation |
| .toString(options?) | (optionally control array style) |
| .between(field, range, logicalOperator?) | Add a between condition |
| .notBetween(field, range, logicalOperator?) | Add a not between condition |
| .equals(field, value, logicalOperator?) | Add an equals (=) condition |
| .notEquals(field, value, logicalOperator?) | Add a not equals (!=) condition |
| .looseEquals(field, value, logicalOperator?) | Add a loose equals (==) condition |
| .strictEquals(field, value, logicalOperator?) | Add a strict equals (===) condition |
| .strictNotEquals(field, value, logicalOperator?) | Add a strict not equals (!==) condition |
| .greaterThan(field, value, logicalOperator?) | Add a greater than (>) condition |
| .greaterThanOrEqual(field, value, logicalOperator?) | Add a greater than or equal (>=) condition |
| .lessThan(field, value, logicalOperator?) | Add a less than (<) condition |
| .lessThanOrEqual(field, value, logicalOperator?) | Add a less than or equal (<=) condition |
| .like(field, value, logicalOperator?) | Add a like condition |
| .ilike(field, value, logicalOperator?) | Add an ilike (case-insensitive like) condition |
| .in(field, values, logicalOperator?) | Add an in condition |
| .notIn(field, values, logicalOperator?) | Add a not in condition |
| .isNull(field, logicalOperator?) | Add an is null condition |
| .isNotNull(field, logicalOperator?) | Add an is not null condition |
| .isEmpty(field, logicalOperator?) | Add an is empty condition |
| .isNotEmpty(field, logicalOperator?) | Add an is not empty condition |
#### Static Helpers
| Method | Description |
| ------------------------------------------------- | ------------------------------------------------------ |
| QueryBuilder.fn(name, ...args) | Create function calls for values (e.g., UPPER(name)) |
| QueryBuilder.validateOperator(operator, value?) | Validate if an operator is compatible with a value |
Supported Operators
| Type | Operators |
| ---------- | ------------------------------------------------------------------ |
| Logical | and, or |
| Comparison | =, ==, ===, !=, !==, >, <, >=, <= |
| Word | starts with, ends with, contains, matches, like, ilike |
| Set | in, not in |
| Range | between, not between |
| Null Check | is null, is not null, is empty, is not empty` |