Skip to content

Utilities

Utils are utility functions that can be used in hooks or anywhere else in your Feathers application. They can be used to perform common tasks such as mutating data, validating input, or formatting output.

Context
Topic
37 of 37
UtilityDescription
addSkip

Adds hook names to context.params.skipHooks so that skippable-wrapped hooks will be bypassed for the current service call. Accepts a single name or an array. Duplicates are automatically removed.

addToQuery

Safely merges properties into a Feathers query object. If a property already exists with a different value, it wraps both in a $and array to preserve both conditions. If the exact same key-value pair already exists, no changes are made. When the added query is itself a pure $and ({ $and: [...] }), its branches are flattened into the target's $and rather than nested.

The added query narrows the target — every condition is kept, so the result matches at most what the target matched. Two conditions on the same property are therefore intersected, never unioned: adding { role: 'b' } to { role: 'a' } matches nothing, it does not become { role: { $in: ['a', 'b'] } }. Use {@link mergeQuery} with its default combine mode when you want to broaden a query instead.

The query filters $select, $limit, $skip and $sort are split off and merged separately, never wrapped in an $and where no adapter would evaluate them. Since addToQuery narrows, they follow the same rules as {@link mergeQuery} in intersect mode: the added query wins for $limit and $skip, $sort is merged key by key (the added query wins per key, the target keeps the leading sort order), and $select becomes the intersection of both. A filter only one side provides is kept as it is.

checkContext

Validates that the hook context matches the expected type(s) and method(s). Throws an error if the context is invalid, preventing hooks from running in unsupported configurations. Typically used internally by other hooks. Also narrows the context type based on the passed options.

The options form takes the same criteria as isContexttype, method, path and id — and reports every one of them that did not match. Only type, method and path narrow the context type.

chunkFind

Use for await to iterate over chunks (pages) of results from a find method.

This function is useful for processing large datasets in batches without loading everything into memory at once. It uses pagination to fetch results in chunks, yielding each page's data array.

contextToJson

Converts a FeathersJS HookContext to a plain JSON object by calling toJSON() if available. This is important when using lodash get/has on the context, since the HookContext class uses getters that may not be enumerable.

createMany

Creates all given items with a single create([...]) call.

If the service does not allow multi creating (its multi option, or an explicit multi option), the items are created with one call per item instead - so it works regardless of the service's multi configuration. A single item is always created with a single call.

defineHooks

TypeScript helper that provides full type inference and autocompletion when defining service hooks. It is an identity function that simply returns its input, but enables your IDE to infer the correct hook context types.

dotifyQuery

Converts the nested properties of a Feathers query into dot notation — { user: { name: 'x' } } becomes { 'user.name': 'x' }. This is the form every Feathers adapter understands, so it is the direction you normally want.

The conversion is query-aware rather than a generic object flatten:

  • operators ($ne, $in, ...) never become path segments
  • $or/$and/$nor/$not branches are converted individually
  • $sort keys are flattened, its directions are kept
  • $select, $limit, $skip and custom operators pass through untouched

A value is only treated as a path when it is a non-empty plain object with at least one non-$ key. Date, RegExp, bson ObjectId, class instances, arrays, primitives and {} are always values. An object that mixes operators and plain keys keeps its operators on the current path: { user: { $ne: null, name: 'x' } } becomes { user: { $ne: null }, 'user.name': 'x' }.

Use descend, exclude or include for properties that legitimately hold an object value. The query is not mutated and is returned unchanged (same reference) when there was nothing to convert.

Nothing is ever dropped when two paths collide. Deep-equal values collapse into one, operator objects with disjoint keys merge, and a genuine contradiction is wrapped in $and — the colliding keys were an implicit AND to begin with. This matches {@link addToQuery}.

eqOrIn

Turns a list of values into a query value that matches any of them: a single remaining value becomes an equality match, everything else a $in. Values are deduplicated first, so ['a', 'a'] collapses to the equality form. An empty list yields { $in: [] }, matching nothing — check for emptiness first if your adapter dislikes an empty IN ().

Deduplication compares primitives with SameValueZero and non-primitives deep-equal, so value wrappers like Date or a mongo ObjectId collapse even though they are distinct references. Note that deep equality ignores key order, so two plain objects with the same entries in a different order count as one.

gateParams

Selects and/or projects params keys according to a declarative path schema, returning a NEW object. General-purpose — no cache knowledge. Typically composed into the cache hook's transformParams option as (p) => gateParams(p, schema, opts).

Paths are resolved with lodash get/has and written with set, so nested values can be picked declaratively ('user.id': true).

params is never mutated: kept values are copied over by reference into the fresh result (safe, since the result is only read/serialized). The one exception is combining a top-level rule with a nested path under the SAME parent in one schema (e.g. { query: true, 'query.x': ... }) — the nested set would then write into the shared parent. Pick one granularity per parent to avoid it.

query is included as-is by DEFAULT (it is always relevant), unless the schema addresses it explicitly — either as query or a nested query.* path.

Keys not mentioned in the schema are KEPT by default, so forgetting a relevant key can only cause a harmless cache miss, never a false hit. Pass dropUnknownParams: true to keep only query and the schema paths.

getDataIsArray

Normalizes context.data into an array for uniform processing. Returns { data, isArray } where data is always an array and isArray indicates whether the original value was already an array.

getExposedMethods

Returns the list of method names that are publicly exposed by a Feathers service. Reads the internal [SERVICE].methods property set during service registration. Throws if the service does not have any exposed methods configured.

getPaginate

Resolves the active pagination options for the current hook context. Checks (in order): context.params.paginate, service.options.paginate, and context.params.adapter.paginate. Returns undefined if pagination is disabled.

getResultIsArray

Normalizes context.result (or context.dispatch) into an array for uniform processing. Handles paginated results by extracting the data array. Returns { result, isArray, key } where key indicates whether 'result' or 'dispatch' was used.

iterateFind

Use for await to iterate over the results of a find method.

This function is useful for iterating over large datasets without loading everything into memory at once. It uses pagination to fetch results in chunks, allowing you to process each item as it is retrieved.

mergeQuery

Properties are combined with a logical operator rather than merged at the value level, so the result is always a valid query: combine always wraps the two queries in $or (broaden — OR has no flat form), while intersect merges non-conflicting properties flat and wraps conflicts in $and (narrow). The special filters $select, $limit, $skip and $sort are merged separately. Inputs are never mutated.

Under combine, branches that constrain the same single property with an equality or an $in are collapsed into a single $in over the union of their values — the same condition, without the $or (opt out with collapseOrToIn: false).

This is well suited to merging a client-provided query with a server-side restriction inside a hook.

mutateData

Applies a transformer function to each item in context.data, updating it in place. If the transformer returns a new object, it replaces the original item. Correctly handles both single-item and array data, preserving the original shape.

mutateResult

Applies a transformer function to each item in context.result (and optionally context.dispatch). Handles paginated results, single items, and arrays transparently. Use the dispatch option to control whether result, dispatch, or both are transformed.

neOrNin

Turns a list of values into a query value that excludes all of them: a single remaining value becomes { $ne: value }, everything else { $nin: values }. Values are deduplicated first, so ['a', 'a'] collapses to the $ne form. An empty list yields { $nin: [] }, excluding nothing — check for emptiness first if your adapter dislikes an empty NOT IN ().

Deduplication compares primitives with SameValueZero and non-primitives deep-equal, so value wrappers like Date or a mongo ObjectId collapse even though they are distinct references. Note that deep equality ignores key order, so two plain objects with the same entries in a different order count as one.

nestifyQuery

Converts the dot-notation properties of a Feathers query into nested objects — { 'user.name': 'x' } becomes { user: { name: 'x' } }. This is the inverse of {@link dotifyQuery}.

The conversion is query-aware rather than a generic object unflatten:

  • $or/$and/$nor/$not branches are converted individually
  • $sort keys are kept in dot notation (nested ones are flattened), because that is the only form the Feathers adapters understand
  • $select, $limit, $skip and custom operators pass through untouched — $select holds paths as values, not as keys
  • a path containing a $-prefixed segment is never split

Use split, exclude or include for keys whose dots are meaningful data. The query is not mutated and is returned unchanged (same reference) when there was nothing to convert.

Nothing is ever dropped when two keys collide. Deep-equal values collapse into one, objects with disjoint keys merge, and a genuine contradiction is wrapped in $and, since the colliding keys were an implicit AND to begin with — this matches {@link addToQuery}. A key whose path is blocked by a non-object value needs no $and at all: it simply stays in dot notation, which is already a valid condition.

Caveat: this direction is best effort and not semantics-preserving on MongoDB, where { user: { name: 'x' } } means document equality while { 'user.name': 'x' } means a subfield match. dotifyQuery is the reliable direction; reach for nestifyQuery when a consumer genuinely needs the nested shape.

patchBatch

Batch patching utility that takes an array of items to be changed and returns an array of arguments to be called with the patch method.

This utility is useful when you need to patch multiple items with varying data in as few requests as possible.

patchMany

Patches all items matching params with a single patch(null, ...) call.

If the service does not allow multi patching (its multi option, or an explicit multi option), the matching items are fetched with find and patched with one call per item instead - so it works regardless of the service's multi configuration.

The params only select which items are affected: the query is consumed by the find - which only collects the ids - and is not applied again for the per-item calls, except $select.

queryDefaults

Adds default properties to a Feathers query — but only for fields the query does not already constrain. Presence is checked with {@link queryHasProperty}, so a field referenced anywhere (including nested in $and/$or/$nor) is left untouched and the caller keeps control over it. The query is treated as the data equivalent of the defaults transformer. Each default is applied independently (per-field).

queryHasProperty

Checks whether a Feathers query contains one or more properties — including properties nested inside $and/$or/$nor arrays. Returns true as soon as any of the given property names is found. The query is not mutated.

removeMany

Removes all items matching params with a single remove(null, ...) call.

If the service does not allow multi removing (its multi option, or an explicit multi option), the matching items are fetched with find and removed with one call per item instead - so it works regardless of the service's multi configuration.

The params only select which items are affected: the query is consumed by the find - which only collects the ids - and is not applied again for the per-item calls, except $select.

replaceData

Replaces context.data wholesale with the given items, preserving the original single-vs-array shape. This is the explicit inverse of getDataIsArray: get the data as an array, modify or replace the items, then write them back.

replaceResult

Replaces context.result (and/or context.dispatch) wholesale with the given items, preserving the original shape: single item, array, or paginated { data }. This is the explicit inverse of getResultIsArray.

simplifyQuery

Normalizes the logical structure of a Feathers query without changing what it matches: empty $and/$or are dropped, duplicate branches removed, nested same-operator branches hoisted ($and-in-$and, pure $or-in-$or), $or branches on the same property collapsed into a single $in, single-value $in/$nin written as an equality/$ne, and branches merged up into the parent where it is safe — all of an $and when no key collides, a single-branch $or. Runs recursively. Inputs are not mutated; a query with nothing to simplify is returned unchanged.

skipResult

Sets context.result to an appropriate empty value based on the hook method. Returns an empty paginated object for paginated find, an empty array for multi operations, or null for single-item operations. Does nothing if a result already exists.

sortQueryProperties

Recursively normalizes a Feathers query object for order-independent comparison. Sorts object keys and sorts arrays within $or, $and, $nor, $not, $in, and $nin operators so that different orderings produce the same result.

This is useful for generating stable cache keys where { $or: [{ a: 1 }, { b: 2 }] } and { $or: [{ b: 2 }, { a: 1 }] } should be treated as equivalent.

stringifyParams

Serializes Feathers params into a stable, deterministic string — built for generating cache keys.

Two normalizations make semantically-equal params produce the same string:

  • sortObject: object keys are sorted recursively.
  • sortArray: elements of query array operators ($or, $and, $nor, $not, $in, $nin) are order-normalized.

Serialization is crash-safe and never throws on values that leak through a params whitelist: circular references become [Circular], functions/undefined/symbol are dropped (like JSON.stringify), BigInt is stringified, and objects with toJSON (e.g. Date, bson ObjectId) are serialized via it.

toPaginated

Ensures a result is in Feathers paginated format ({ total, limit, skip, data }). If the input is already paginated, it is returned as-is. If it is a plain array, it is wrapped in a paginated object with total and limit set to the array length.

transformParams

Safely applies a transformParams function to a params object. If no function is provided, the original params are returned unchanged. The function receives a shallow copy of params, so the original is not mutated.

unpaginate

Extracts the items from a find result that may or may not be paginated. A plain array is returned as-is, a paginated result yields its data, and undefined/null yields an empty array - so a find can be consumed without knowing whether pagination is enabled for the service or disabled via params.paginate: false.

This is the lossy counterpart to toPaginated: total, limit and skip are dropped, so unpaginate(toPaginated(x)) round-trips but the reverse does not. The returned array MUST be treated as read-only - it is the input array or the paginated result's data, never a copy.

waitForServiceEvent

Wait for a service event to fire and resolve with the emitted record. Useful in tests to await the result of an asynchronous service event, a bit like promisify for Feathers events.

Curried: bind the app (and optional defaults) once, then call the returned function per service/event. Resolves with a [data, { event, context }] tuple: data is typed as the service's record type, and event is the union of the requested events.

Feathers emits events as emit(event, record, context) and fires one event per record, so each resolution carries a single record and its HookContext.

walkQuery

Walks every property of a Feathers query (including nested $and/$or/$nor arrays) and calls the walker function for each one. The walker receives the property name, operator, value, path, and a stop function, and can return a replacement value. Calling stop() halts the traversal early. Returns a new query only if changes were made.

zipDataResult

Pairs each item in context.data with its corresponding item in context.result by index. Handles both single-item and array data, normalizing them into an array of { data, result } pairs. Only works in after/around hooks for create, update, and patch methods.

Released under the MIT License.