Skip to content

setPath ​

Category
Export size
min 1.35 kB · gzip 0.75 kB
See also

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.

ts
  import { 
setPath
} from 'feathers-utils/helpers';

Example ​

ts
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
ts
/** 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
ArgumentTypeDescription
targetT
pathPropertyPath
valueunknown

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:

ts
const params = { query: { active: true } }

await app.service('todos').find(params) // hook: setPath(context, 'params.query.userId', 1)

params.query // => { active: true } — untouched

Coming 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 what context holds;
  • paths through __proto__, constructor or prototype are ignored instead of written.

Released under the MIT License.