QubeeQueryBuilder
The query builder, as an Angular service
Wraps the framework-agnostic QueryBuilder from @qubeejs/core and adds
the reactivity the core deliberately leaves to adapters: every read is a
Signal, so templates, computed(), effect() and httpResource()
track the query state without any glue.
Two kinds of members:
- Mutators (
setResource,addFilter,nextPage, …) write to the sharedQubeeSignalStoreand returnthisfor chaining. They throw the core’sUnsupported*Errors when the active driver lacks the capability. - Readers (
uri,state,currentPage,hasNextPage, …) are computed signals. They are read by calling them —qb.hasNextPage()— which is exactly how the 3.x methods were called, but they now re-evaluate only when the state changes.
Every mutator delegates to the core QueryBuilder: what each one emits per driver, and which drivers support it, is in the core reference ↗.
@Component({ template: ` <button [disabled]="qb.isFirstPage()" (click)="qb.previousPage()">Prev</button> <span>Page {{ qb.currentPage() }} of {{ qb.totalPages() ?? '…' }}</span> <button [disabled]="qb.isLastPage()" (click)="qb.nextPage()">Next</button> `})export class UsersComponent { protected readonly qb = inject(QubeeQueryBuilder);
// Re-fetches whenever the URI changes; idle until a resource is set. protected readonly users = httpResource<unknown>(() => this.qb.uri());
constructor() { this.qb.setResource('users').addSort('created_at', SortEnum.DESC); }}Properties
Section titled “Properties”| Member | Type | Description |
|---|---|---|
currentPage |
Signal<number> |
The current page number. |
hasNextPage |
Signal<boolean> |
Whether a next page exists. true until a paginated response has synced the bounds (conservative default), then page < lastPage. |
hasPreviousPage |
Signal<boolean> |
Whether a previous page exists (page > 1). |
isFirstPage |
Signal<boolean> |
Whether the current page is the first page. |
isLastPage |
Signal<boolean> |
Whether the current page is the last page. false until a paginated response has synced the bounds, so a “Next” button bound to it stays enabled before the first fetch. |
paginationHeaders |
Signal<Record<string, string> | null> |
HTTP request headers the active driver wants applied to the request. null for drivers that pass all pagination metadata on the URL — every driver except PostgREST configured with PaginationModeEnum.RANGE, which yields { 'Range-Unit': 'items', 'Range': 'from-to' }. Safe to spread into a headers map unconditionally. Compared by value, so a state change that leaves the range untouched — a synced lastPage, say — does not re-trigger an httpResource() whose request spreads these headers. |
state |
Signal<QueryBuilderState> |
The complete query-builder state. The snapshot is deeply frozen: read it freely, mutate it never. |
totalPages |
Signal<number | undefined> |
The total number of pages reported by the most recent paginated response, or undefined until QubeePaginator.paginate() has synced one. The 3.x method threw PaginationNotSyncedError here; a signal is read in templates, where undefined is the useful answer. lastPage() keeps the throwing behaviour for imperative navigation. |
uri |
Signal<string | undefined> |
The URI for the current state, or undefined until a resource is set. Recomputed on every state change. undefined is the idle sentinel httpResource() understands, so httpResource(() => qb.uri()) stays idle until setResource() is called and re-fetches on every mutation after. Always equal to generateUri: both go through the core builder, which appends the parameters set with setParam after the driver’s own. |
Methods
Section titled “Methods”addEmbedded()
Section titled “addEmbedded()”addEmbedded(relation: string, ...columns: string[]): thisAdd an embedded resource to the select statement (PostgREST, OData, Directus)
qb.addEmbedded('author', 'id', 'name') .addEmbedded('comments') .addSelect('title');// PostgREST → select=title,author(id,name),comments(*)Repeated calls for the same relation merge-dedup the columns. Does not reset the page.
| Parameter | Type | Description |
|---|---|---|
relation |
string |
The related table / foreign-key name as the backend sees it. |
...columns |
string[] |
Optional column projection; omit for relation(*) |
Throws UnsupportedEmbeddedError — If the active driver does not support embedded resources.
addFields()
Section titled “addFields()”addFields(model: string, fields: string[]): thisAdd fields to the select statement for the given model (JSON:API and Spatie)
| Parameter | Type | Description |
|---|---|---|
model |
string |
Model that holds the fields. |
fields |
string[] |
Fields to select. |
Throws UnsupportedFieldSelectionError — If the active driver does not support per-model field selection.
addFilter()
Section titled “addFilter()”addFilter(field: string, ...values: (string | number | boolean)[]): thisAdd a filter with the given value(s)
Multi-value calls merge per field. Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
field |
string |
Name of the field to filter. |
...values |
(string | number | boolean)[] |
The needle(s). |
Throws UnsupportedFilterError — If the active driver does not support filters.
addFilterOperator()
Section titled “addFilterOperator()”addFilterOperator(field: string, operator: FilterOperatorEnum, ...values: (string | number | boolean)[]): thisAdd a filter with an explicit operator
Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
field |
string |
Name of the field to filter. |
operator |
FilterOperatorEnum |
The filter operator to apply. |
...values |
(string | number | boolean)[] |
The value(s) for the filter. |
Throws UnsupportedFilterOperatorError — If the active driver does not support filter operators, or this operator.
Throws InvalidFilterOperatorValueError — If the values do not fit the operator (e.g. BTW without exactly two).
addIncludes()
Section titled “addIncludes()”addIncludes(...models: string[]): thisAdd related entities to include in the request
| Parameter | Type | Description |
|---|---|---|
...models |
string[] |
Models to include. |
Throws UnsupportedIncludesError — If the active driver does not support includes.
addSelect()
Section titled “addSelect()”addSelect(...fields: string[]): thisAdd flat field selection (select=col1,col2)
| Parameter | Type | Description |
|---|---|---|
...fields |
string[] |
Fields to select. |
Throws UnsupportedSelectError — If the active driver does not support flat field selection.
addSort()
Section titled “addSort()”addSort(field: string, order: SortEnum): thisAdd a field with a sort criteria
Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
field |
string |
Field to use for sorting. |
order |
SortEnum |
A value from the SortEnum enumeration. |
Throws UnsupportedSortError — If the active driver does not support sorts.
deleteEmbedded()
Section titled “deleteEmbedded()”deleteEmbedded(...relations: string[]): thisRemove embedded resources from the current state (PostgREST, OData, Directus)
Removes the whole relation entry, columns included.
| Parameter | Type | Description |
|---|---|---|
...relations |
string[] |
Relation names to remove. |
Throws UnsupportedEmbeddedError — If the active driver does not support embedded resources.
deleteFields()
Section titled “deleteFields()”deleteFields(fields: Fields): thisDelete selected fields for the given models (JSON:API and Spatie)
qb.deleteFields({ users: ['email', 'password'], address: ['zipcode'] });| Parameter | Type | Description |
|---|---|---|
fields |
Fields |
Object mapping model names to field arrays to remove. |
Throws UnsupportedFieldSelectionError — If the active driver does not support per-model field selection.
deleteFieldsByModel()
Section titled “deleteFieldsByModel()”deleteFieldsByModel(model: string, ...fields: string[]): thisDelete selected fields for the given model (JSON:API and Spatie)
qb.deleteFieldsByModel('users', 'email', 'password');| Parameter | Type | Description |
|---|---|---|
model |
string |
Model that holds the fields. |
...fields |
string[] |
Fields to delete from the state. |
Throws UnsupportedFieldSelectionError — If the active driver does not support per-model field selection.
deleteFilters()
Section titled “deleteFilters()”deleteFilters(...filters: string[]): thisRemove the given filters from the state
Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
...filters |
string[] |
Filter field names to remove. |
Throws UnsupportedFilterError — If the active driver does not support filters.
deleteIncludes()
Section titled “deleteIncludes()”deleteIncludes(...includes: string[]): thisRemove related models from the state
| Parameter | Type | Description |
|---|---|---|
...includes |
string[] |
Models to remove. |
Throws UnsupportedIncludesError — If the active driver does not support includes.
deleteOperatorFilters()
Section titled “deleteOperatorFilters()”deleteOperatorFilters(...fields: string[]): thisRemove operator filters by field name
Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
...fields |
string[] |
Field names of operator filters to remove. |
Throws UnsupportedFilterOperatorError — If the active driver does not support filter operators.
deleteParams()
Section titled “deleteParams()”deleteParams(...keys: string[]): thisRemove extra query parameters set with setParam (all drivers)
Resets the page to 1. Calling it with no keys is a no-op.
| Parameter | Type | Description |
|---|---|---|
...keys |
string[] |
Parameter keys to remove. |
deleteSearch()
Section titled “deleteSearch()”deleteSearch(): thisRemove the search term from the state
Resets the page to 1.
Throws UnsupportedSearchError — If the active driver does not support search.
deleteSelect()
Section titled “deleteSelect()”deleteSelect(...fields: string[]): thisRemove flat field selections from the state
| Parameter | Type | Description |
|---|---|---|
...fields |
string[] |
Fields to remove from selection. |
Throws UnsupportedSelectError — If the active driver does not support flat field selection.
deleteSorts()
Section titled “deleteSorts()”deleteSorts(...sorts: string[]): thisRemove sort rules from the state
Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
...sorts |
string[] |
Fields used for sorting to remove. |
Throws UnsupportedSortError — If the active driver does not support sorts.
firstPage()
Section titled “firstPage()”firstPage(): thisNavigate to the first page
Never throws. Idempotent when already on page 1.
generateUri()
Section titled “generateUri()”generateUri(): stringBuild the URI for the current state using the active driver
Synchronous: URI construction performs no I/O. For a reactive read that
follows every state change, use the uri signal instead.
Returns — The generated URI
Throws Error — If the resource is unset.
Throws UnselectableModelError — If fields were selected on a model that is neither the resource nor included.
goToPage()
Section titled “goToPage()”goToPage(n: number): thisNavigate directly to the specified page
| Parameter | Type | Description |
|---|---|---|
n |
number |
Target page number. |
Throws InvalidPageNumberError — If n is not a positive integer, or exceeds the last page when the bounds are known.
lastPage()
Section titled “lastPage()”lastPage(): thisNavigate to the last page known from the most recent paginated response
Throws PaginationNotSyncedError — If no paginated response has been synced yet — read totalPages first to guard.
nextPage()
Section titled “nextPage()”nextPage(): thisNavigate to the next page
Never throws. No-op at the known last page. Pair with hasNextPage for a disable-state binding.
previousPage()
Section titled “previousPage()”previousPage(): thisNavigate to the previous page
Never throws. Floored at page 1. Pair with hasPreviousPage for a disable-state binding.
reset()
Section titled “reset()”reset(): thisClear the state and reset the query builder to a fresh, clean condition
setBaseUrl()
Section titled “setBaseUrl()”setBaseUrl(baseUrl: string): thisSet the base URL to prepend to generated URIs
| Parameter | Type | Description |
|---|---|---|
baseUrl |
string |
The base URL (e.g. https://api.example.com). |
setLimit()
Section titled “setLimit()”setLimit(limit: number): thisSet the items-per-page number
Validation is delegated to the active request strategy because the
accepted range is driver-specific: nestjs-paginate accepts -1 as a
“fetch all” sentinel, the others require a positive integer. Resets the
page to 1.
| Parameter | Type | Description |
|---|---|---|
limit |
number |
Number of items per page. |
Throws InvalidLimitError — If the value is not accepted by the active driver.
setPage()
Section titled “setPage()”setPage(page: number): thisSet the page the backend will use to paginate the result set
| Parameter | Type | Description |
|---|---|---|
page |
number |
Page number. |
Throws InvalidPageNumberError — If page is not a positive integer.
setParam()
Section titled “setParam()”setParam(key: string, ...values: (string | number | boolean)[]): thisSet a query parameter no driver models (all drivers)
For what a backend accepts beyond the driver’s own parameters — a
top-level status, an include on a driver without includes. Appended
after the driver’s parameters, so it works with every driver and is not
gated by capabilities:
qb.setParam('status', 'failed'); // → status=failedqb.setParam('ids', 'a,b', 'c'); // → ids=a%2Cb,cThe key is emitted verbatim — never build one from end-user input — and each value is percent-encoded like a filter value. Setting a key again replaces its values; calling it with no values is a no-op. Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
key |
string |
The parameter name, emitted verbatim. |
...values |
(string | number | boolean)[] |
The value(s), joined with , |
Throws ParamCollisionError — From generateUri and uri, when the key collides with a parameter the driver emits.
setResource()
Section titled “setResource()”setResource(resource: string): thisSet the API resource to run the query against
Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
resource |
string |
Resource name (e.g. 'users' produces /users). |
Throws InvalidResourceNameError — If resource is not a non-empty string.
setSearch()
Section titled “setSearch()”setSearch(search: string): thisSet the search term for full-text search
Resets the page to 1.
| Parameter | Type | Description |
|---|---|---|
search |
string |
The search term. |
Throws UnsupportedSearchError — If the active driver does not support search.