@nodebrick/nodebrick-api
v1.4.19
Published
Nodebrick API module, providing express server
Downloads
3
Readme
@nodebrick/nodebrick-api
__
Description
- api port
Install
$ npm install --save @nodebrick/nodebrick-api
Usage
Authorization
Options
The nodebrick-api has a middleware that will parse the options
query parameter.
The options query parameter can have none or all the following objects{fieds:{}, filters:{}, sort{}}
How each string is defined for fields
, filters
and sort
is covered further down.
The parsed options (options
of type nodebrick-core/src/models/IResourceOptions
) will be available in the controller using the @Options
decorator.
It will also be available in the application context under IResourceOptionContext
, i.e applicationContext.get(IResourceOptionsContext)
Please read the nodebrick-core context documentation for more information on contexts.
All options use the following way of selecting objects
Use a comma-separated list to select multiple fields.
Use a/b
to select a property b
that is nested within object a
; use a/b/c
to select a property c
nested within b
nested within a
.
Use a sub-selector to request a set of specific sub-fields of arrays or objects by placing expressions in parentheses ( )
.
For example: {fields:{items(id,author/email)}}
returns only the item ID and author's email for each element in the items array.
You can also specify a single sub-field, where {fields:{items(id)}}
is equivalent to {fields:{items/id}}
.
Sorting
Use a comma-separated list to select multiple fields.
For example:
{sort:{email:DESC)}}
will sort on email field, DESC{sort:{contact/email:DESC)}}
will sort on the contact.email field, DESC
IOptionSort
, parsed version of the sort option will be available in the application context under IOptionSortContext
, i.e applicationContext.get(IOptionSortContext)
In the controller you will also be able to access the filters via the options.sort
, options being the object decorated with @Options
in the method parameters.
Example
?options={sort:{name:DESC}}
The following fields object will be available
{
"createdAt": "2019-01-24T21:38:55.011Z",
"updatedAt": "2019-01-24T21:38:55.011Z",
"deletedAt": null,
"uuid": "b989ad79-c0ab-47d8-ac1a-745c8ae52f61",
"name": "wellington",
"population": 418500,
"date": "2019-01-24T21:38:55.011Z"
},
{
"createdAt": "2019-02-01T00:55:19.285Z",
"updatedAt": "2019-02-01T00:55:19.285Z",
"deletedAt": null,
"uuid": "eda0172b-a1b4-40b2-a3bd-d3ca684317aa",
"name": "tauranga",
"population": 141600,
"date": "2019-02-01T00:55:19.285Z"
},
{
"createdAt": "2019-02-01T00:55:19.051Z",
"updatedAt": "2019-02-01T00:55:19.051Z",
"deletedAt": null,
"uuid": "8708664d-211b-48c6-bae4-016ddaa96217",
"name": "tauranga",
"population": 141600,
"date": "2019-02-01T00:55:19.051Z"
},
{
"createdAt": "2019-02-01T00:55:18.803Z",
"updatedAt": "2019-02-01T00:55:18.803Z",
"deletedAt": null,
"uuid": "e7d1af07-3016-4903-bc19-6ef406a58428",
"name": "tauranga",
"population": 141600,
"date": "2019-02-01T00:55:18.803Z"
},
{
"createdAt": "2019-02-01T00:55:17.679Z",
"updatedAt": "2019-02-01T00:55:17.679Z",
"deletedAt": null,
"uuid": "11938bdd-1687-4ea1-a763-7a4333e90117",
"name": "tauranga",
"population": 141600,
"date": "2019-02-01T00:55:17.679Z"
}
...
Fields selection
Use a comma-separated list to select multiple fields.
For example:
{fields:{items}}
returns only the item object/array/property.{fields:{items(id,author/email)}}
returns only the item ID and author's email for each element in the items array.
IOptionFields
, parsed version of the fields option will be available in the application context under IOptionFieldsContext
, i.e applicationContext.get(IOptionFieldsContext)
Example
?options={fields:{author,library(name/common,address(city,country/code)),price}}
The following fields object will be available
{
author: true,
library: {
name: {
common: true
},
address: {
city: true,
country: {
code: true
}
}
},
price: true
}
This object is used by the interceptor to trim down the resource sent back.
If a specified field doesn't exists on the resource it will error.
Filters selection
Use a comma-separated list to select multiple filters.
A filter is structured like property:operator_value
for one value or property:operator_value1_value2...
if multiple values are requires by the operator
You can see the list of all available operator on the nodebrick-core documentation
For example:
{filters:{email:$like_%xavier.martin%)}}
will ask to return all object having email like%xavier.martin%
{filters:{contactemail:$like_%xavier.martin%)}}
will ask to return all object having contact.email like%xavier.martin%
OptionFilters
, parsed version of the filter option will be available in the application context under OptionFiltersContext
, i.e applicationContext.get(OptionFiltersContext)
In the controller you will also be able to access the filters via the options.filters
, options being the object decorated with @Options
in the method parameters.
The filters object has keys as the properties you filtered on. The value looking like this:
property: string,
value: [string]|[string,string],
operator: FilterOperatorsEnum,
operatorSQL: [string]|[string, string]
Example
?options={filters:{author:$like_stephen king,book:$nilike_it,date:$between_1995-01-01_2018-12-12}}
The following filters object will be available:
{
author: {
property: "author",
value: [
"stephen king"
],
operator: "$like",
operatorSQL: [
"LIKE"
]
},
book: {
property: "book",
value: [
"it"
],
operator: "$nilike",
operatorSQL: [
"NOT ILIKE"
]
},
date: {
property: "date",
value: [
"1995-01-01",
"2018-12-12"
],
operator: "$between",
operatorSQL: [
"BETWEEN",
"AND"
]
}
}
Use this object to alter your query.
Count & Paginate
We have implemented two types of paginations, seek and offset.
Offset pagination requires the record you want to start from and a limit (number of records to return).
This is VERY inefficient. It requires the database to also work with on all the previous records.
This is somehow the default almost everywhere.
Seek (extension of keyset pagination, have a look) pagination where you give the ID to get result after or before. This also use the limit (number of item to return). This is efficient as the database will jump exactly to this record and the next number and work on those.
How to Implement it in our backend
So the nodebrick-api has written those contexts, how can they be leveraged?
An example is the nodebrick-database as show here:
public async getManyWithQueryBuilder(): Promise<IModel[]>
{
// create the query builder
const query: SelectQueryBuilder<IModel> = this.manager.createQueryBuilder(IModel, "mod_a");
// ask the service the count value
// this is executed only if the client requested a count, see later
await this._dbService.filter(query);
await this._dbService.sort(query);
await this._dbService.paginate(query);
await this._dbService.count(query);
// or you can do
await this._dbService.applyOptions(query);
return query.getMany();
}
and that's it.
The count
method will count records, if requested by the client
The paginate
method will paginate the result if requested by the client
How to request count or pagination
Count
Add the following query parameters:
- count: boolean
returns a count field (integer) in the response object, sibling to the data property
A count property will be added to the response with the following structure
{
"do_count": <boolean>,
"count": <integer>
}
Example
Request:/resource?count=true
Response:
{
"data": {},
"count": {
"do_count": true,
"count": 40
}
}
Offset
Add the following query parameters:
- start: integer
returns start at the record counts - limit: integer
return this number of records
Example
Request:/resource?limit=5&start=5
Response:
{
"data": {},
"pagination": {
"type": "offset",
"limit": 5,
"start": 5,
"prev_url": "/resource?limit=5&start=0",
"self_url": "/resource?limit=5&start=5",
"next_url": "/resource?limit=5&start=10"
}
}
Seek
Add the following query parameters:
- limit: integer.
Return this number of records - before: UUID
Return the records before this UUID OR - after: UUID
Return the records after this UUID
Example
Request:/resource?limit=5&before=c8dae5da-0b64-4b55-ab56-95f59b9eb8b2
Response:
{
"data": {},
"pagination": {
"type": "seek",
"limit": 5,
"after": null,
"before": "c8dae5da-0b64-4b55-ab56-95f59b9eb8b2",
"self": "/nodebrick-api/options?limit=5&before=c8dae5da-0b64-4b55-ab56-95f59b9eb8b2&options=%7Bfields%3A%7Bauthor,library(name%2Fcommon,address(city,country%2Fcode)),price%7D,filters%3A%7Bauthor%3A%24like_stephen%20king,book%3A%24nilike_it,date%3A%24between_1995-01-01_2018-12-12%7D%7D",
"prev": "/nodebrick-api/options?limit=5&before=undefined&options=%7Bfields%3A%7Bauthor,library(name%2Fcommon,address(city,country%2Fcode)),price%7D,filters%3A%7Bauthor%3A%24like_stephen%20king,book%3A%24nilike_it,date%3A%24between_1995-01-01_2018-12-12%7D%7D"
}
}