| name | aurora-criteria |
| description | Aurora Criteria Pattern - Complete guide for QueryStatement usage in Aurora/NestJS. Trigger: When implementing queries, filters, searches, pagination, or complex data retrieval.
|
| license | MIT |
| metadata | {"author":"aurora","version":"1.0","auto_invoke":"Building QueryStatement filters, implementing pagination, creating search queries"} |
When to Use
Use this skill when:
- Implementing GET/FIND queries in services or handlers
- Building filters for REST or GraphQL endpoints
- Adding pagination to data retrieval
- Creating complex search functionality
- Working with QueryStatement parameters
- Need to filter, sort, or limit results
What is QueryStatement?
QueryStatement is Aurora's standardized interface for building complex database queries using the Criteria Pattern.
It provides a unified API for filtering, sorting, pagination, and data selection across all repositories.
Core Structure
interface QueryStatement {
where?: JSON;
attributes?: JSON;
include?: string[];
order?: JSON;
group?: JSON;
limit?: number;
offset?: number;
distinct?: boolean;
col?: string;
}
Critical Patterns
⚠️ OPERATOR SYNTAX (CRITICAL!)
Operators MUST be quoted keys wrapped in square brackets:
{ "where": { "age": { "[gte]": 18 } } }
{ "where": { "name": { "[startsWith]": "Carlos" } } }
{ "where": { "age": { gte: 18 } } }
{ "where": { "name": { startsWith: "Carlos" } } }
Usage in Services
@Injectable()
export class TeslaGetModelsService {
constructor(private readonly repository: TeslaIModelRepository) {}
async main(
queryStatement?: QueryStatement,
constraint?: QueryStatement,
cQMetadata?: CQMetadata,
): Promise<TeslaModel[]> {
return await this.repository.get({
queryStatement,
constraint,
cQMetadata,
});
}
}
Usage in Queries
export class TeslaGetModelsQuery {
constructor(
public readonly queryStatement?: QueryStatement,
public readonly constraint?: QueryStatement,
public readonly cQMetadata?: CQMetadata,
) {}
}
WHERE Operators Reference
Logical Operators
{
where: {
"[and]": [
{ status: "PRODUCTION" },
{ year: { "[gte]": 2020 } }
]
}
}
{
where: {
"[or]": [
{ status: "PRODUCTION" },
{ status: "PREPRODUCTION" }
]
}
}
{
where: {
"[not]": {
deletedAt: { "[is]": null }
}
}
}
Equality & Nulls
{ where: { id: "uuid-here" } }
{ where: { id: { "[eq]": "uuid-here" } } }
{ where: { status: { "[ne]": "DISCLAIMER" } } }
{ where: { deletedAt: { "[is]": null } } }
{ where: { deletedAt: { "[is]": "[not]null" } } }
Comparison Operators
{ where: { year: { "[gt]": 2020 } } }
{ where: { year: { "[gte]": 2020 } } }
{ where: { year: { "[lt]": 2025 } } }
{ where: { year: { "[lte]": 2024 } } }
{
where: {
year: {
"[gte]": 2020,
"[lte]": 2024
}
}
}
Range Operators
{ where: { year: { "[between]": [2020, 2024] } } }
{ where: { year: { "[notBetween]": [2000, 2010] } } }
Set Operators
{
where: {
status: {
"[in]": ["PRODUCTION", "PREPRODUCTION"]
}
}
}
{
where: {
status: {
"[notIn]": ["DISCLAIMER", "CONCEPTION"]
}
}
}
String Pattern Matching
{ where: { name: { "[like]": "%Model%" } } }
{ where: { name: { "[notLike]": "Admin%" } } }
{ where: { name: { "[iLike]": "%roadster%" } } }
{ where: { name: { "[notILike]": "test%" } } }
{ where: { name: { "[startsWith]": "Model" } } }
{ where: { name: { "[endsWith]": "S" } } }
{ where: { name: { "[substring]": "Air" } } }
{ where: { sku: { "[regexp]": "^[A-Z]{3}-[0-9]+$" } } }
{ where: { sku: { "[notRegexp]": "test" } } }
{ where: { sku: { "[iRegexp]": "abc" } } }
{ : { : { : } } }
Column Comparison
{
where: {
updatedAt: { "[col]": "createdAt" }
}
}
Array Operators (PostgreSQL)
{
where: {
tags: { "[overlap]": ["react", "node"] }
}
}
{
where: {
tags: { "[contains]": ["graphql"] }
}
}
{
where: {
roles: { "[any]": ["admin", "editor"] }
}
}
Other QueryStatement Properties
Field Selection (attributes)
{
attributes: ['id', 'name', 'status']
}
{
attributes: {
exclude: ['deletedAt', 'createdAt']
}
}
Eager Loading (include)
{
include: [{ association: 'model' }, { association: 'units' }]
}
{
include: {
model: true,
units: true
}
}
Sorting (order)
{
order: [
{ createdAt: 'asc' }
]
}
{
order: [
{ createdAt: 'desc' }
]
}
{
order: [
{ status: 'asc' },
{ year: 'desc' },
{ name: 'asc' }
]
}
Pagination
{
limit: 25
}
{
offset: 0
}
{
offset: 0,
limit: 25
}
{
offset: 25,
limit: 25
}
Grouping
{
group: ['status', 'year']
}
Distinct
{
distinct: true
}
Complete Examples
Example 1: Simple Filter
const queryStatement: QueryStatement = {
where: {
isActive: true,
year: { "[gte]": 2020 }
}
};
await this.repository.get({ queryStatement });
Example 2: Complex Search with Pagination
const queryStatement: QueryStatement = {
where: {
"[and]": [
{ status: { "[in]": ["PRODUCTION", "PREPRODUCTION"] } },
{ name: { "[iLike]": "%model%" } },
{ deletedAt: { "[is]": null } }
]
},
order: [
{ year: 'desc' },
{ name: 'asc' }
],
offset: 0,
limit: 10
};
await this.repository.get({ queryStatement });
Example 3: With Relations and Field Selection
const queryStatement: QueryStatement = {
where: {
status: "PRODUCTION"
},
attributes: ['id', 'name', 'year', 'status'],
include: [{ association: 'units' }],
order: [
{ year: 'desc' }
]
};
await this.repository.get({ queryStatement });
Example 4: Constraint Pattern (Security)
const queryStatement: QueryStatement = {
where: {
name: { "[startsWith]": "Model" }
}
};
const constraint: QueryStatement = {
where: {
isActive: true,
deletedAt: { "[is]": null }
}
};
await this.repository.get({
queryStatement,
constraint
});
Example 5: GraphQL/REST Usage
query GetModels($query: QueryStatement) {
teslaGetModels(query: $query) {
id
name
year
status
}
}
{
"query": {
"where": {
"year": { "[gte]": 2020 }
},
"order": [
{ "year": "desc" }
],
"limit": 10
}
}
{
"query": {
"where": {
"year": { "[gte]": 2020 }
},
"order": [
{ "year": "desc" }
],
"limit": 10
}
}
Common Patterns
Paginated List
const queryStatement: QueryStatement = {
where: {
deletedAt: { "[is]": null }
},
order: [
{ createdAt: 'desc' }
],
offset: (page - 1) * pageSize,
limit: pageSize
};
Search by Multiple Fields
const queryStatement: QueryStatement = {
where: {
"[or]": [
{ name: { "[iLike]": `%${searchTerm}%` } },
{ sku: { "[iLike]": `%${searchTerm}%` } },
{ description: { "[iLike]": `%${searchTerm}%` } }
]
}
};
Date Range Filter
const queryStatement: QueryStatement = {
where: {
createdAt: {
"[gte]": startDate,
"[lte]": endDate
}
}
};
Active Records Only
const queryStatement: QueryStatement = {
where: {
"[and]": [
{ isActive: true },
{ deletedAt: { "[is]": null } }
]
}
};
Decision Tree
Need to filter data?
├─ Single condition → Use simple where: { field: value }
├─ Multiple AND conditions → Use implicit AND or "[and]"
├─ Multiple OR conditions → Use "[or]": [...]
├─ Range (min/max) → Use "[gte]" and "[lte]"
├─ List of values → Use "[in]": [...]
└─ Pattern matching → Use "[like]", "[iLike]", or "[startsWith]"
Need to sort?
└─ Use order: [{ field: 'asc'|'desc' }]
Need pagination?
└─ Use offset + limit
Need specific fields?
└─ Use attributes: [...]
Need relations?
└─ Use include: [...]
Need to ensure security?
└─ Use constraint parameter (separate from queryStatement)
Best Practices
✅ DO
- Always use quoted operators with brackets:
"[gte]", "[startsWith]"
- Use
constraint for system-enforced filters (security, soft-deletes)
- Use
queryStatement for user-provided filters
- Combine operators in same field:
{ year: { "[gte]": 2020, "[lte]": 2024 } }
- Use
[iLike] for case-insensitive searches
- Always filter soft-deleted records:
deletedAt: { "[is]": null }
- Validate user input before building QueryStatement
❌ DON'T
- Don't use unquoted operators:
gte: ❌ Use "[gte]": ✅
- Don't use operators without brackets:
"gte" ❌ Use "[gte]" ✅
- Don't trust user-provided
constraint (always set server-side)
- Don't forget pagination for large datasets
- Don't expose sensitive fields in
attributes
- Don't use
[like] with user input without validation (SQL injection risk)
Resources
- Aurora Core Types:
@aurorajs.dev/core exports QueryStatement
- MCP Server: See
src/@api/mcp/mcp.server.ts for full operator reference
- GraphQL Schema: See
src/@api/graphql.ts for QueryStatement interface
- Test Examples: See
test/acceptance/tesla/*.e2e-spec.ts for real usage
Related Skills
aurora-project-structure - Understand where queries live
typescript - Type-safe QueryStatement construction
aurora-cli - Regenerate repositories after schema changes