# Utilities

# callingParams

Build params for a service call.

  • Arguments

    • {Object} options
    • {Object} context
Argument Type Default Description
options Object How to construct params for service call.
context Object The context of the hook which will make the service call.
options Argument Type Default Description
query Object The params.query for the calling params.
propNames Array< String > [] The names of the props in context.params to include in the new params.
newProps Object {} Additional props to add to the new params.
hooksToDisable Array< String > [] The names of hooks to disable during the service call. populate, fastJoin, softDelete and stashBefore are supported.
ignoreDefaults Boolean Ignore the defaults propNames and newProps.
  • Returns

    • {Object} newParams
Name Type Description
newParams Object The params for the service call.
  • Example

    const { callingParams, callingParamsDefaults } = require('feathers-hooks-common');
    // Authentication props to always copy. Suitable for feathers-authentication-management.
    callingParamsDefaults(['provider', 'authenticated', 'user', 'isVerified']);
    
    async function myCustomHook(context) {
      // ...
      const result = await service.find(callingParams({
        query: { { id: { $in: [1, 2, 3] } } },
        propNames: ['customProp'],
        newProps: { mongoose: ... },
        hooksToDisable: 'populate'
      }), context);
      // ...
    }
    
  • Details

    When calling another service within a hook, consideration must be given to what the params should be for the called service. For example, should the called service see that a client is making the call, or the server? What authentication and authorization information should be provided? You can use this convenience function to help create that params.

    The properties provider, authenticated and user are the standard authentication properties used by Feathers. They are copied automatically.

    These defaults and others can be changed app-wide by calling the callingParamsDefaults utility.

# callingParamsDefaults

Set defaults for building params for service calls with callingParams.

  • Arguments

    • {Array< String >} propNames
    • {Object} newProps
Argument Type Default Description
propNames Array< String > The names of the props in context.params to automatically include in the new params.
newProps Object Additional props to add to the new params.
  • Example

    const { callingParams, callingParamsDefaults } = require('feathers-hooks-common');
    // Authentication props to always copy. Suitable for feathers-authentication-management.
    // Only hooks will be calling `callingParams`. Set a flag so other hooks recognize such a call.
    callingParamsDefaults(['provider', 'authenticated', 'user', 'isVerified'], { _calledByHook: true });
    
    async function myCustomHook(context) {
      // ...
      const result = await service.find(callingParams({
        query: { { id: { $in: [1, 2, 3] } } },
        propNames: ['customProp'],
        newProps: { mongoose: ... },
        hooksToDisable: 'populate'
      }), context);
      // ...
    }
    
  • Details

    When calling another service within a hook, consideration must be given to what the params should be for the called service. For example, should the called service see that a client is making the call, or the server? What authentication and authorization information should be provided? You can use this convenience function to help create that params.

    The properties provider, authenticated and user are the standard authentication properties used by Feathers. They are copied automatically.

    These defaults and others can be changed app-wide by calling the callingParamsDefaults utility.

# checkContext

Restrict a hook to run for certain methods and method types.

  • Arguments
    • {Object} context
    • {String} [ type ]
    • {Array< String >} [ methods ]
    • {String} [ label ]
Argument Type Default Description
context Object The hook context.
type String all types The service type allowed - before, after.
methods Array< String > all methods The service methods allowed - find, get, update, patch, remove.
label String 'anonymous' Name of hook to use with throw.
  • Example

    const { checkContext } = require('feathers-hooks-common');
    
    function myHook(context) {
      checkContext(context, 'after', ['create', 'remove']);
      ...
    }
    
    module.exports = { before: {
        create: [ myHook ] // throws
    } };
    
    // checkContext(hook, 'before', ['update', 'patch'], 'hookName');
    // checkContext(hook, null, ['update', 'patch']);
    // checkContext(hook, 'before', null, 'hookName');
    // checkContext(hook, 'before');
    
  • Details

    Its important to ensure the hook is being used as intended. checkContext let's you restrict the hook to a hook type and a set of service methods.

# combine

Sequentially execute multiple sync or async hooks.

before after methods multi details
yes yes all n/a source
  • Arguments
    • {Array< Function >} hookFuncs
Argument Type Default Description
hookFuncs Array<Function > Hooks, used the same way as when you register them.
  • Example

    const { combine, createdAt, updatedAt } = require('feathers-hooks-common')
    
    async function myCustomHook(context) {
      const newContext = await combine(
        setNow('createdAt'),
        setNow('updatedAt')
      ).call(this, context)
      return newContext
    }
    
  • Details

    combine has the signature of a hook, but is primarily intended to be used within your custom hooks, not when registering hooks.

The following is a better technique to use when registering hooks.

const workflow = [createdAt(), updatedAt(), ...];

module.exports = { before: {
  update: [...workflow],
  patch: [...workflow],
} };

# getItems

Get the records in context.data or context.result

  • Arguments
    • {Object} context
Argument Type Default Description
context Object The hook context.

Returns

  • {Array< Object > | Object | undefined} records
Name Type Description
records Array< Object > Object
  • Example

    const { getItems, replaceItems } = require('feathers-hooks-common');
    
    const insertCode = code => context {
      const items = getItems(context);
      if (Array.isArray(items)) {
        items.forEach(item => { item.code = code; });
      } else {
        items.code = code;
      }
      replaceItems(context, items);
    };
    
    module.exports = { before: {
      create: insertCode('a')
    } };
    
  • Details

    getItems gets the records from the hook context: context.data (before hook) or context.result[.data] (after hook).

# replaceItems

Replace the records in context.data or context.result[.data].

  • Arguments

    • {Object} context
    • {Array< Object > | Object} records
Argument Type Default Description
context Object The hook context.
records Array< Object > Object The new records.
  • Example

    const { getItems, replaceItems } = require('feathers-hooks-common');
    
    const insertCode = code => context {
      const items = getItems(context);
      if (Array.isArray(items)) {
        items.forEach(item => { item.code = code; });
      } else {
        items.code = code;
      }
      replaceItems(context, items);
    };
    
    module.exports = { before: {
      create: insertCode('a')
    } };
    
  • Details

    replaceItems replaces the records in the hook context: context.data (before hook) or context.result[.data] (after hook).

# makeCallingParams

Build context.params for service calls.

Tip: You should prefer using the callingParams utility to makeCallingParams.

  • Arguments

    • {Object} context
    • {Object} [ query ]
    • {Array< String > | String} [ include ]
    • {Object} [ inject ]
Argument Type Default Description
context Object The existing hook context.
query Object The context.params.query for the new context.
include Array< String > The names of the props in context to include in the new context.
inject Object Additional props to add to the new context.
  • Returns

    • {Object} newContext
Variable Type Default Description
newContext Object The new context created.
  • Example

    const { makeCallingParams } = require('feathers-hooks-common');
    
    async function myCustomHook(context) {
      // ...
      const result = await service.find(makeCallingParams(
        context, { id: { $in: [1, 2, 3] } }, 'user',
        { _populate: false, mongoose: ... }
      ));
      // ...
    }
    
  • Details

    When calling another service within a hook, consideration must be given to what the context.params should be for the called service. For example, should the called service see that a client is making the call, or the server? What authentication and authorization information should be provided? You can use this convenience function to help create that context.params.

    The value context.params._populate: 'skip' is automatically added to skip any fastJoin or populate hooks registered on the called service. Set it to false, like in the example above, to make those hooks run.

# paramsForServer

Pass an explicit context.params from client to server. Client-side.

  • Arguments
    • {Object} params
    • {Array< String >} [ whitelist ]
Argument Type Default Description
params Object The context.params to use for the service call, including any query object.
whitelist dot notation all props in context.params Names of the props in context.params to transfer to the server. This is a security feature. All props are transferred if no whitelist is provided.
  • Example

    // client
    const { paramsForServer } = require('feathers-hooks-common')
    
    service.update(
      id,
      data,
      paramsForServer({
        query: { dept: 'a' },
        populate: 'po-1',
        serialize: 'po-mgr'
      })
    )
    
    // server
    const { paramsFromClient } = require('feathers-hooks-common')
    
    module.exports = {
      before: {
        all: [paramsFromClient('populate', 'serialize', 'otherProp'), myHook]
      }
    }
    
    // myHook's `context.params` will now be
    // { query: { dept: 'a' }, populate: 'po-1', serialize: 'po-mgr' } }
    
  • Details

    By default, only the context.params.query object is transferred from a Feathers client to the server, for security among other reasons. However you can explicitly transfer other context.params props with the client utility function paramsForServer in conjunction with the paramsFromClient hook on the server.

    This technique also works for service calls made on the server.

    The data is transfered using `context.params.query.$client`. If that field already exists, it must be an Object.

# runHook

Let's you call a hook right after the service call.

  • Arguments
    • {Object} [ hookContext ]
    • {Function} hookFunc
Argument Type Default Description
hookContext Object {} The context for hookFunc.
hookFunc Function The hook to run.
  • Example

    const { keep, runHook } = require('feathers-hooks-common');
    
    user.get(...)
      .then( runHook()(keep('name', 'address.state')) )
      .then(data => ...); // [{ name: 'Marshall', address: { state: 'UT' }}]
    
    const data = await user.get(...);
    const result = await runHook()(data)(keep('name', 'address.state'));
    
    const { fastJoin, runHook } = require('feathers-hooks-common')
    const runHookFinds = runHook({ app: app, method: 'find' })
    
    const paymentsRecords = [
      { _id: 101, amount: 100, patientId: 1 },
      { _id: 102, amount: 105, patientId: 1 },
      { _id: 103, amount: 110, patientId: 1 },
      { _id: 104, amount: 115, patientId: 2 },
      { _id: 105, amount: 120, patientId: 3 },
      { _id: 106, amount: 125, patientId: 3 }
    ]
    await payments.create(paymentsRecords)
    
    const patientsRecords = [
      { _id: 1, name: 'John' },
      { _id: 2, name: 'Marshall' },
      { _id: 3, name: 'David' }
    ]
    await patients.create(patientsRecords)
    
    const paymentResolvers = {
      joins: {
        patient: () => async payment => {
          payment.patient = (
            await patients.find({
              query: {
                id: payment.patientId
              }
            })
          )[0]
        }
      }
    }
    
    await payments
      .find()
      .then(runHookFinds(fastJoin(paymentResolvers)))
      .then(data => console.log(data))
    
    // log
    ;[
      { _id: 101, amount: 100, patientId: 1, patient: { _id: 1, name: 'John' } },
      { _id: 102, amount: 105, patientId: 1, patient: { _id: 1, name: 'John' } },
      { _id: 103, amount: 110, patientId: 1, patient: { _id: 1, name: 'John' } },
      {
        _id: 104,
        amount: 115,
        patientId: 2,
        patient: { _id: 2, name: 'Marshall' }
      },
      { _id: 105, amount: 120, patientId: 3, patient: { _id: 3, name: 'David' } },
      { _id: 106, amount: 125, patientId: 3, patient: { _id: 3, name: 'David' } }
    ]
    
  • Details

    Hooks are normally registered for a service, e.g. in project/src/services /posts/posts.hooks.js. This is nice and simple when, for example, all the find hooks have to run for every find call.

    The conditional hooks can be used when hooks have to be conditionally run based on the current environment. For example, we can discard the password field when the call is made by a client.

    However things are not always so straightforward. There can be that one call for which we want to join specific records. We could add a conditional hook that runs just for that one call, however we may soon find ourselves with a second and a third special case.

    runHook is designed for such cases. Instead of having to register a conditioned hook, it allows us to run the hook in a .then() right after the service call.