Get a properly formatted query for your API platform backend by using SpdrQueryBuilder and its methods.
This library helps you type and validate your query for basic filters. Some of them like "Boolean" or "Numeric" can be done with qb.search()
since they have the same format.
You can easily build your own types by extending SpdrParam
, or by implementing SpdrParamInterface
. Like if you need ElasticSearch specific filters or else.
All the following examples are using a new instance of the SpdrQueryBuilder.
Params are split internally into 3 sections so that you can reset them separately: Params, Sort and Pagination. Sort and Pagination are destructive. It means that if you pass a sort on a same property, or if you set the page size 2 times in a row, it will override the last param. This is way simpler to use since you usually don't want to sort twice on the same property, and you don't want to have multiple page size params in your query. You don't have to take care of the replacement yourself. Note that only concerns sort and pagination. For now, normal params are "manual".
import { SpdrQueryBuilder } from '@sonicfury/spdr-query-builder';
const qb = new SpdrQueryBuilder();
qb.exists('transportFees');
qb.query
equals 'exists[transportFees]=true'
This query will return all items where transport fees is not null.
It builds the param with the true
value by default, but you can set it to false.
qb.exists('transportFees', false);
qb.query
equals 'exists[transportFees]=false'
qb.search('description', ['shirt']);
qb.query
equals description=shirt
As you can see, the second parameter is an array of values. The behaviour in case of multiple values is the following:
qb.search('color', ['blue', 'red', 'green'])
qb.query
equals color[]=blue&color[]=red&color[]=green
This behaviour is the default for SQL 'IN' equivalent, but only works with the exact
search strategy (see API platform docs by following the provided link for further information).
Also, if you have a custom filter that needs a custom operand for multiple values ('OR' case), you can pass it as third parameter:
qb.search('color', ['blue', 'red', 'green'], '||');
qb.query
equals color[]=blue||color[]=red||color[]=green
That won't affect the query builder's global operand.
import {DateOperator} from "./spdrParam";
const date = new Date('12/25/2022')
qb.date('addedAt', DateOperator.after, date);
qb.query
equals addedAt[after]=2022-12-25
This will return all items added after 12/25/2022.
There are 4 operators that you can use.
DateOperator.before;
DateOperator.after;
DateOperator.strictlyBefore;
DateOperator.strictlyAfter;
import {RangeOperator} from "./spdrParam";
qb.range('price', RangeOperator.lt, 10);
qb.query
equals price[lt]=10
This will return all items with price lower than 10. There are 5 operators that you can use.
RangeOperator.lt;
RangeOperator.lte; // 'lower than or equal'
RangeOperator.gt;
RangeOperator.gte; // 'greater than or equal'
RangeOperator.between
For RangeOperator.between
you have to provide a second value.
qb.range('price', RangeOperator.between, 10, 100);
qb.query
equals price[between]=10..100
import {OrderOperator} from "./spdrParam";
qb.order('name', OrderOperator.asc);
qb.query
equals order[name]=asc
You can use the OrderOperator.desc
to sort descending.
You can handle pagination too.
API Platform: Let client enable pagination
qb.pagination();
qb.query
equals pagination=true
You can disable pagination by passing false
as the first argument.
If you want to use a custom parameter name, just pass it as the second argument.
qb.enablePagination(false, 'enable_pagination');
qb.query
equals enable_pagination=false
API Platform: Define page index client-side
qb.pageIndex(14);
qb.query
equals page=14
qb.pageIndex(14, 'offset');
qb.query
equals offset=14
API Platform: Define items per page client-side
qb.pageSize(15);
qb.query
equals itemsPerPage=15
qb.pageSize(15, 'limit')
qb.query
equals limit=15
The query builder takes as many parameters as you want and builds automatically a query string that you can append to your API url.
import {SpdrQueryBuilder} from "./spdrQueryBuilder";
const qb = new SpdrQueryBuilder()
.exists('isActive', true)
.search('name', ['John Doe', 'Jane Doe'])
.date('addedAt', DateOperator.strictlyBefore, date)
.range('orders', RangeOperator.between, 0, 10)
.order('name', OrderOperator.asc)
.pageIndex(2, '_page')
.pageSize(10);
// for example
this.domainService.get(qb.query);
qb.query
equals exists[isActive]=true&name[]=John Doe&name[]=Jane Doe&orders[between]=0..10&order[name]=asc&_page=2&itemsPerPage=10
You can remove all params matching a property by calling qb.remove(property, type)
with property as a string
and type as a SpdrParamType
.
By passing only the property as a string, all params matching the property will be removed, no matter what their type is.
Passing a SpdrParamType
lets you be more precise and remove only params matching the property of the passed type.
const qb = new SpdrQueryBuilder()
.exists('isActive', true)
.search('name', ['John Doe', 'Jane Doe'])
.date('addedAt', DateOperator.strictlyBefore, date)
.range('orders', RangeOperator.between, 0, 10)
.order('name', OrderOperator.asc)
.pageIndex(2, '_page')
.pageSize(10);
At this point qb.query
equals exists[isActive]=true&name[]=John Doe&name[]=Jane Doe&addedAt[strictly_before]=${isoString}&orders[between]=0..10&order[name]=asc&_page=2&itemsPerPage=10
qb.remove('name', SpdrParamType.param)
would get youexists[isActive]=true&addedAt[strictly_before]=${isoString}&orders[between]=0..10&order[name]=asc&_page=2&itemsPerPage=10
. It has only removed thesearch
filters on the 'name' property.qb.remove('name')
would get youexists[isActive]=true&addedAt[strictly_before]=${isoString}&orders[between]=0..10&_page=2&itemsPerPage=10
. You can see it has removed theorder
filter on the 'name' as well.
You can also clear the query builder by calling qb.clear(type)
.
This lets you start a new query and keep the previous ones in history.
The behaviour is somewhat similar to the qb.remove
method, in the way you can call the method with no arguments to remove all filters, or with a SpdrParamType
to remove only filters of the passed type.
const qb = new SpdrQueryBuilder()
.exists('isActive', true)
.search('name', ['John Doe', 'Jane Doe'])
At this point, qb.query
equals exists[isActive]=true&name[]=John Doe&name[]=Jane Doe
qb.clear()
qb.query
is now empty.
qb
.search('name', ['John Doe', 'Jane Doe'])
.date('addedAt', DateOperator.strictlyBefore, date)
Now, qb.query
equals name[]=John Doe&name[]=Jane Doe&addedAt[strictly_before]=${isoString}
qb.previousQuery
equals exists[isActive]=true&name[]=John Doe&name[]=Jane Doe
qb.clear()
.search('name', ['John Doe', 'Jane Doe'])
.date('addedAt', DateOperator.strictlyBefore, date)
.range('orders', RangeOperator.between, 0, 10)
At the end, qb.query
equals name[]=John Doe&name[]=Jane Doe&addedAt[strictly_before]=${isoString}&orders[between]=0..10
qb.previousQuery
equals name[]=John Doe&name[]=Jane Doe&addedAt[strictly_before]=${isoString}
But you can still get back to the first one by looking into qb.history()
You can also clear history by calling qb.clearHistory()
. This lets you get a fresh start with the same instance (if you set a custom operand for example).
If you need a custom operand to be applied to your query, you have 2 ways to set it. First way is in the constructor:
const CUSTOM_OPERAND = '&&';
const qb = new SpdrQueryBuilder(CUSTOM_OPERAND);
qb
.exists('isActive', true)
.search('name', ['John Doe', 'Jane Doe'])
qb.query
equals exists[isActive]=true&&name[]=John Doe&&name[]=Jane Doe
Second way is the setOperand
method:
const qb = new SpdrQueryBuilder()
qb
.setOperand('&&')
.exists('isActive', true)
.search('name', ['John Doe', 'Jane Doe'])
qb.query
equals exists[isActive]=true&&name[]=John Doe&&name[]=Jane Doe
You can use this method multiple times, but it will override the operand each time:
const qb = new SpdrQueryBuilder('&&')
qb
.exists('isActive', true)
.search('name', ['John Doe'])
.setOperand('!!')
.date('addedAt', DateOperator.strictlyBefore, date)
.setOperand('&')
.range('orders', RangeOperator.between, 0, 10)
qb.query
equals exists[isActive]=true&name=John Doe&addedAt[strictly_before]=${isoString}&orders[between]=0..10
because the last time sets the operand to &
You may want to set manually the params. For example, you can get the params lists, save them into localStorage
and set them back when needed.
The query builder exposes a getter and a setter for each list, rebuilding the query automatically every time you'll set a list.
Example:
import {SpdrQueryBuilder} from "./spdrQueryBuilder";
const qb = new SpdrQueryBuilder()
const qb2 = new SpdrQueryBuilder()
qb
.exists('isActive', true)
.search('name', ['John Doe', 'Jane Doe']);
localStorage.setItem('params', JSON.stringify(qb.params))
const jsonParams = localStorage.getItem('params')
qb2.params = JSON.parse(params)
qb
and qb2
will output the same query. This also works with qb.sortParams
and qb.paginationParams
.
Only basic default filters are supported but you can easily create your own SpdrParam
by extending SpdrParam
abstract
class. Plus, the SpdrQueryBuilder
expects an implementation of the SpdrParamInterface
so you can even create
your own abstract class that will suit you best.
import {Operator, SpdrParamInterface} from "./spdrParam";
class MyCustomParam implements SpdrParamInterface {
query: string;
operator: Operator = Operator.equals;
constructor(property: string, value: string) {
this.query = `${property}${this.operator}${value}`;
}
}
You'll have to implement a custom method in you own SpdrQueryBuilder
definition, and that should eventually give:
const qb = new CustomQueryBuilder();
qb.customMethod('myCustomParam', 'myCustomValue');
qb.query
equals myCustomParam=myValue