setPath
Writes value at path, copying every object on the way — the copy-on-write that leaves a caller's params untouched.
target itself IS mutated (a hook owns context and re-assigns context.params); everything below it is replaced by a shallow copy, so untouched branches keep their identity. That matters for values Feathers compares by reference, such as params.connection, which a deep clone would break.
Missing segments are created as an array when the next key is an integer index, otherwise as a plain object. Paths through __proto__, constructor or prototype are ignored.
import { setPath } from 'feathers-utils/helpers';Example
import { setPath } from 'feathers-utils/helpers'
// in a hook: params.query is copied, params.connection keeps its identity
setPath(context, 'params.query.userId', context.params.user.id)
setPath({}, 'a[0].b', 1) // => { a: [{ b: 1 }] }Type declaration
Show Type Declarations
/** Keys that would let a path write reach `Object.prototype`. */
export declare const isUnsafeKey: (key: PropertyKey) => boolean
/**
* Writes `value` at `path`, copying every object on the way — the copy-on-write
* that leaves a caller's `params` untouched.
*
* `target` itself IS mutated (a hook owns `context` and re-assigns
* `context.params`); everything below it is replaced by a shallow copy, so
* untouched branches keep their identity. That matters for values Feathers
* compares by reference, such as `params.connection`, which a deep clone would
* break.
*
* Missing segments are created as an array when the next key is an integer
* index, otherwise as a plain object. Paths through `__proto__`, `constructor`
* or `prototype` are ignored.
*
* @example
* ```ts
*
*
* // in a hook: params.query is copied, params.connection keeps its identity
* setPath(context, 'params.query.userId', context.params.user.id)
*
* setPath({}, 'a[0].b', 1) // => { a: [{ b: 1 }] }
* ```
*
* @see https://utils.feathersjs.com/helpers/set-path.html
*/
export declare const setPath: <T>(
target: T,
path: PropertyPath,
value: unknown,
) => T
/**
* Writes `value` at `path`, mutating every object on the way. Use it only on a
* value you own — a freshly built item or one a transformer was handed to
* change; for anything reachable from a caller's `params`, use {@link setPath}.
*
* @example
* ```ts
* setPathInPlace(item, 'createdAt', new Date())
* ```
*/
export declare const setPathInPlace: <T>(
target: T,
path: PropertyPath,
value: unknown,
) => T| Argument | Type | Description |
|---|---|---|
| target | T | |
| path | PropertyPath | |
| value | unknown |
Why copy-on-write
Feathers hands a hook the caller's params. The caller may reuse them for the next call, so writing into params.query changes that call too. setPath only ever writes into target itself and into fresh copies below it:
const params = { query: { active: true } }
await app.service('todos').find(params) // hook: setPath(context, 'params.query.userId', 1)
params.query // => { active: true } — untouchedComing from lodash
_.set writes into every object on the path. Replacing it with setPath keeps the result the same but stops leaking the write into objects shared with the caller. Two differences:
- objects on the path are shallow copies afterwards, so a reference taken before the call (
const q = context.params.query) no longer points at whatcontextholds; - paths through
__proto__,constructororprototypeare ignored instead of written.
