Limits of $limit=0 for No‑Op Responses in FeathersJS Services
0 reputation · 07 Oct 2021, 02:55 UTC
0 reputation · 07 Oct 2021, 02:55 UTC
Determine whether explicitly setting $limit: 0 in a FeathersJS query reliably avoids any database interaction for low‑traffic services across the officially supported adapters.
The FeathersJS documentation states that omitting $limit falls back to an adapter‑specific default (often 10 or 100), which can cause unnecessary payloads. While many adapters translate $limit: 0 into a LIMIT 0 clause and return an empty array, the exact behavior—especially whether the query is still sent to the underlying store—varies by adapter and is not guaranteed.
$limit: 0 guarantee that no database query is executed on Sequelize, Mongoose and Knex adapters?$limit value without configuring each service individually?$limit (e.g., 1 or 2) affect overall cost when a client must paginate through many pages to retrieve the full dataset?35775 reputation · 07 Oct 2021, 05:01 UTC
No. Setting $limit: 0 in a FeathersJS query does not reliably avoid database interaction. For the officially supported Sequelize, Mongoose, and Knex adapters, the value is passed through to the underlying store as a LIMIT 0 (or .limit(0)) clause, and a query is still executed — it simply returns an empty result set. There is no short-circuit in these adapters that skips the round-trip when the limit is zero.
The likely explanation for the confusion is the documentation's focus on pagination defaults: omitting $limit falls back to an adapter-specific default (commonly 10 or 100), so setting $limit: 0 feels like it should mean "fetch nothing." What is confirmed by adapter behavior is that "fetch nothing" still means "ask the database for zero rows" — the connection is acquired, the statement is planned and executed, and an empty array (or paginated envelope with total) comes back.
Note that third-party or community adapters may behave differently. Only the Sequelize, Mongoose, and Knex adapters are covered here; verify any other adapter against its source before relying on this.
Enable query logging and call find with $limit: 0:
// Sequelize: new Sequelize(..., { logging: console.log })
// Mongoose: mongoose.set('debug', true)
// Knex: knex({ ..., debug: true })
await app.service('messages').find({ query: { $limit: 0 } });
// Expect a SELECT ... LIMIT 0 (or equivalent) in the logsIf the goal is a true no-op (e.g., a cheap existence or count probe), use an explicit hook that returns early instead of relying on $limit: 0.
Register an application-level before hook that clamps the limit for every service, rather than configuring each service individually:
app.hooks({
before: {
find: [
(context) => {
const MAX = 100;
const q = context.params.query;
if (q && typeof q.$limit !== 'undefined' && Number(q.$limit) > MAX) {
q.$limit = MAX;
}
return context;
}
]
}
});Caveats: this affects every find call, so services that legitimately need larger pages should be exempted (check context.path or a custom flag) or override the value in their own hooks, which run after app-level hooks.
Using $limit: 1 or 2 when the client ultimately needs the full dataset multiplies the number of round-trips. Each page still incurs connection overhead, query planning, and network latency, so total cost rises roughly linearly with page count. For large exports, prefer a higher limit, cursor-based pagination, or streaming rather than tiny pages.
$limit: 0 gives you an empty payload, not a free request — the database is still hit on Sequelize, Mongoose, and Knex. If you need a genuine no-op, implement it as an early-returning hook; if you need cost control, clamp $limit globally and choose page sizes that balance payload size against request count.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.