Querying

Every adapter exposes the same _find query language — a FeathersJS-style object that drives filtering, pagination, sorting, field selection and relation loading. The same shape works in code and as an HTTP query string.

In code
const result = await service._find({
  status: { $in: ['active', 'pending'] },
  price: { $gte: 10, $lte: 100 },
  $sort: { createdAt: -1 },
  $limit: 20,
});
Over HTTP
GET /products?status[$in][]=active&status[$in][]=pending&price[$gte]=10&$sort[createdAt]=-1&$limit=20

The response is a paginated envelope:

interface PaginatedResponse<D> {
  total: number;
  $limit: number;
  $skip: number;
  data: D[];
}

Queries by database

The _find API is the same everywhere, but relation loading and case-insensitive matching differ slightly. Pick your database to see the exact example:

Adapter: Prisma or TypeORM

Relations load with $include. $iLike is fully case-insensitive on PostgreSQL.
PostgreSQL · _find()
await service._find({
  status: { $in: ['active', 'pending'] },
  price: { $gte: 10, $lte: 100 },
  name: { $iLike: 'phone' },        // case-insensitive match
  $include: { reviews: true },      // eager-load relations
  $sort: { createdAt: -1 },
  $limit: 20,
});
MySQL · _find()
await service._find({
  status: { $in: ['active', 'pending'] },
  price: { $gte: 10, $lte: 100 },
  name: { $iLike: 'phone' },        // case-insensitive match
  $include: { reviews: true },      // eager-load relations
  $sort: { createdAt: -1 },
  $limit: 20,
});
SQLite · _find()
await service._find({
  status: { $in: ['active', 'pending'] },
  price: { $gte: 10, $lte: 100 },
  name: { $iLike: 'phone' },        // case-insensitive match
  $include: { reviews: true },      // eager-load relations
  $sort: { createdAt: -1 },
  $limit: 20,
});
MongoDB · _find()
await service._find({
  status: { $in: ['active', 'pending'] },
  price: { $gte: 10, $lte: 100 },
  name: { $regex: 'phone', $options: 'i' }, // pattern match
  $populate: 'reviews',                      // populate relations
  $sort: { createdAt: -1 },
  $limit: 20,
});

Special parameters

ParameterDefaultPurpose
$limit20Maximum documents to return
$skip0Offset for pagination
$sortSort map, e.g. { createdAt: -1 } (-1 desc, 1 asc)
$selectProject a subset of fields
$populateMongoose — populate relations
$includePrisma / TypeORM — eager-load relations
Pagination, sorting & selection
await service._find({
  $select: ['name', 'price'],
  $sort: { createdAt: -1 },
  $limit: 10,
  $skip: 20,
});

Operators

Filter values can be plain (equality) or an operator object. The operators below are available on every adapter (Prisma and TypeORM share the FeathersJS set; Mongoose maps to native MongoDB operators).

OperatorMeaning
$eq / $neEqual / not equal
$gt / $gteGreater than / greater-or-equal
$lt / $lteLess than / less-or-equal
$in / $ninIn / not in a list
$like / $notLikeCase-sensitive pattern match
$iLike / $notILikeCase-insensitive pattern match
$or / $andLogical combinators
$regexRegular expression (Mongoose)
Combining operators
await service._find({
  price: { $gte: 10, $lte: 100 },
  name: { $iLike: 'phone' },
  $or: [{ category: 'audio' }, { brand: 'acme' }],
});

Relations

Use $populate on Mongoose and $include on Prisma / TypeORM:

Mongoose
await catsService._find({ $populate: 'owner' });
Prisma / TypeORM
await catsService._find({ $include: { owner: true } });

Case-insensitive matching differs by database. $iLike is fully supported on PostgreSQL; MySQL is case-insensitive by default depending on collation; SQLite has no built-in case-insensitive LIKE for non-ASCII text.

Calling the API over HTTP

The same query object works as a URL query string. Serialise the nested operators with qs — it matches the parser configured by NestExtendedModule.forRoot({ queryParser }) on the server.

Build the query string
import qs from 'qs';
 
const query = {
  status: { $in: ['active', 'pending'] },
  price: { $gte: 10, $lte: 100 },
  $sort: { createdAt: -1 },
  $limit: 20,
};
 
const search = qs.stringify(query, { encodeValuesOnly: true });
// status[$in][0]=active&status[$in][1]=pending&price[$gte]=10&price[$lte]=100&$sort[createdAt]=-1&$limit=20

fetch

fetch
const res = await fetch(`/products?${search}`);
const { total, data } = await res.json();

Axios

Pass the query object as params and let qs serialise it so the nested operators survive:

axios
import axios from 'axios';
import qs from 'qs';
 
const api = axios.create({
  baseURL: '/api',
  paramsSerializer: (params) => qs.stringify(params, { encodeValuesOnly: true }),
});
 
const { data } = await api.get('/products', {
  params: {
    status: { $in: ['active', 'pending'] },
    price: { $gte: 10, $lte: 100 },
    $sort: { createdAt: -1 },
    $limit: 20,
  },
});
// data => { total, $limit, $skip, data: [...] }

Next steps