Page Contents

Overview

A query is a read operation on models that returns a set of data or results. As we introduced in Working with data, repositories add behavior to models. Once you have them set up, you can query instances using a Node.js API and a REST API with filters as outlined in the following table. Filters specify criteria for the returned data set. The capabilities and options of the two APIs are the same. The only difference is the syntax used in HTTP requests versus Node function calls. In both cases, LoopBack models return JSON.

Query Model API (Node) REST API
Find all model instances using specified filters. find(filter, options?) Where filter is a JSON object containing the query filters. GET /modelName?filter...
Find first model instance using specified filters. findOne(filter, options?) Where filter is a JSON object containing the query filters. GET /modelName/findOne?filter...
Find instance by ID. findById(id, filter?, options?) Where optional filter is a JSON object containing the query filters. GET /modelName/modelID

LB4 supports standard REST syntax and also “stringified JSON” in REST queries. Please read on to see more details and examples of different types of filter and example usages of REST API.

Filters

LoopBack supports a specific filter syntax: it’s a lot like SQL, but designed specifically to serialize safely without injection and to be native to JavaScript.

LoopBack 4 supports the following kinds of filters:

More additional examples of each kind of filter can be found in the individual articles on filters (for example Where filter). Those examples show different syntaxes in REST and Node.js.

The following table describes LoopBack’s filter types:

Filter type Type Description
fields Object, Array, or String Specify fields to include in or exclude from the response. See Fields filter.
include String, Object, or Array Include results from related models, for relations such as belongsTo and hasMany. See Include filter.
limit Number Limit the number of instances to return. See Limit filter.
order String Specify sort order: ascending or descending. See Order filter.
skip (offset) Number Skip the specified number of instances. See Skip filter.
where Object Specify search criteria; similar to a WHERE clause in SQL. See Where filter.

The following is an example of using the find() method with both a where and a limit filter:

await accountRepository.find({where: {name: 'John'}, limit: 3});

Equivalent using REST:

/accounts?filter[where][name]=John&filter[limit]=3

Syntax

In both Node.js API and REST, you can use any number of filters to define a query.

Node.js syntax

Specify filters as the first argument to find*():

{ filterType: spec, filterType: spec, ... }

There is no theoretical limit on the number of filters you can apply.

Where:

  • filterType is the filter: where, include, order, limit, skip, or fields.
  • spec is the specification of the filter: for example for a where filter, this is a logical condition that the results must match. For an include filter it specifies the related fields to include.

REST syntax

Specify filters in the HTTP query string:

/modelName?filter=[filterType1]=val1&filter[filterType2]=val2...

or

/modelName/id?filter=[filterType1]=val1&filter[filterType2]=val2...

The number of filters that you can apply to a single request is limited only by the maximum URL length, which generally depends on the client used.

If the filter gets too long, you can encode it. For example:

const filter = {
  include: [
    {
      relation: 'orders',
      scope: {
        include: [{relation: 'manufacturers'}],
      },
    },
  ],
};

encodeURIComponent(JSON.stringify(filter));

the url would be:

/customers?filter=<encodeResult>

Using “stringified” JSON in REST queries

Instead of the standard REST syntax described above, you can also use “stringified JSON” in REST queries. To do this, simply use the JSON specified for the Node.js syntax, as follows:

?filter={ Stringified-JSON }

where Stringified-JSON is the stringified JSON from Node.js syntax. However, in the JSON all text keys/strings must be enclosed in quotes (“).

For example: GET /api/activities/findOne?filter={"where":{"id":1234}}

Build filters with FilterBuilder

Besides writing the filter yourself, you can also use FilterBuilder to help you create or combine where clauses.

For example, you can build the filter

const filter = {
  where: {id: 3},
  include: [{relation: 'orders', scope: {where: {name: 'toy'}}]
};

with the FilterBuilder:

import FilterBuilder from '@loopback/repository';
...
const filterBuilder = new FilterBuilder();
    filterBuilder
    .include(
      {relation: 'orders', scope: {where: {name: 'ray'}}},
    )
    .where({id:3})
    .build();

Another usage of FilterBuilder is to combine the where clause by using FilterBuilder.impose. For example,

const filter = new FilterBuilder().limit(5).where({id: 101});
filter.impose({where: {id: 999, name: 'LoopBack'}});

The where clauses is combined as:

{
  limit: 5,
  where: {
      and: [{id: 101}, {id: 999, name: 'LoopBack'}],
  }
}

This also can be done with the WhereBuilder. See more examples in the Where Filter page.

Sanitizing filter and data objects

Prohibits hidden/protected properties from being searched

Hidden or protected properties can expose sensitive information if they are allowed to be searched. LoopBack introduces prohibitHiddenPropertiesInQuery setting at datasource/model level to control if hidden/protected properties can be used in the where object. By default, its value is true. For example,

datasources/db.datasources.config.json

{
  "db": {
    "name": "db",
    "connector": "memory",
    "prohibitHiddenPropertiesInQuery": true
  }
}

With the following model definition:

@model(
settings:{hidden:['secret']}}
)
export class MyModel extends Entity {
  // other props

  @property({
    type: 'string',
  })
  secret?: string;
  //...
}

myModelRepository.find({where: {secret: 'guess'}}); will be sanitized as myModelRepository.find({where: {}}; and a warning will be printed on the console:

Potential security alert: hidden/protected properties ["secret"] are used in query.

Reports circular references

If the filter object has circular references, LoopBack throws an error as follows:

{
  message: 'The query object is circular',
  statusCode: 400,
  code: 'QUERY_OBJECT_IS_CIRCULAR'
}

Constrains the maximum depth of query and data objects

Deep filter objects may be mapped to very complex queries that can potentially break your application. To mitigate such risks, LoopBack allows you to configure maxDepthOfQuery and maxDepthOfData in datasource/model settings. The default value is 12. Please note the depth is calculated based on the level of child properties of an JSON object. For example:

datasources/db.datasources.config.json

{
  "db": {
    "name": "db",
    "connector": "memory",
    "maxDepthOfQuery": 5,
    "maxDepthOfData": 16
  }
}

If the filter or data object exceeds the maximum depth, an error will be reported:

{
  message: 'The query object exceeds maximum depth 5',
  statusCode: 400,
  code: 'QUERY_OBJECT_TOO_DEEP'
}