Giter VIP home page Giter VIP logo

feathers-hooks's Introduction

Feathers Hooks

Important: feathers-hooks is included in Feathers (@feathersjs/feathers) v3 and later and does not have to be loaded and configured separately anymore.

Greenkeeper badge

Build Status Code Climate Test Coverage Dependency Status Download Status Slack Status

Middleware for Feathers service methods

Documentation

Please refer to the Feathers hooks documentation for more details on:

  • The philosophy behind hooks
  • How you can use hooks
  • How you can chain hooks using Promises
  • Params that are available in hooks
  • Hooks that are bundled with feathers and feathers plugins

Quick start

feathers-hooks allows to register composable middleware functions when a Feathers service method executes. This makes it easy to decouple things like authorization and pre- or post processing and error handling from your service logic.

To install from npm, run:

$ npm install feathers-hooks --save

Then, to use the plugin in your Feathers app:

const feathers = require('feathers');
const hooks = require('feathers-hooks');

const app = feathers().configure(hooks());

Then, you can register a hook for a service:

// User service
const service = require('feathers-memory');

module.exports = function(){
  const app = this;

  let myHook = function(options) {
    return 
  }

  // Initialize our service
  app.use('/users', service());

  // Get our initialize service to that we can bind hooks
  const userService = app.service('/users');

  // Set up our before hook
  userService.hooks({
    before(hook){
      console.log('My custom before hook ran!');
    }
  });
}

License

Copyright (c) 2016 Feathers contributors

Licensed under the MIT license.

feathers-hooks's People

Contributors

abraaoalves avatar corymsmith avatar couac avatar creiger avatar daffl avatar eddyystop avatar ekryski avatar greenkeeper[bot] avatar greenkeeperio-bot avatar j2l4e avatar kiaragrouwstra avatar kulakowka avatar marshallswain avatar mattchewone avatar myknbani avatar supasate avatar t2t2 avatar tfussell avatar thebarndog avatar timmensch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

feathers-hooks's Issues

Make sure hook promises propagate their errors

@aroc pointed out that promises with a final .catch can swallow their errors which means that hooks chained like

app.service('todos').before({
   create(hook) {
      throw new Error('Some error');
   }
});

app.service('todos').before({
   create(hook) {
      hook.shouldRun = false;
   }
});

May not work as expected.

Defining a service hook without an app/before app initialization?

I'd like to define a hook without calling app.service('name'), directly on the service. Something like this:

var User = knex('user', config.feathersKnex).extend({
  before: {
    create: [authHooks.hashPassword('password')]
  }
});

Is this possible? It seems like if I run that code, the hook is simply ignored.

Create a `populate` hook

It's really common to populate related entities. This hook would probably take a service name and an id and then automatically populate a value.

For example:

const defaults = {};

module.exports = function(options) {
  options = Object.assign({}, defaults, options);

  return function(hook) {
    if (hook.type === 'after') {
      return new Promise((resolve, reject) => {

        function populateSender(message) {
          return hook.app.service('users').get(message.sentBy, hook.params).then(user => {
            message.sentBy = user;
            return message;
          }).catch(reject);
        }

        if (Array.isArray(hook.result.data)) {
          return Promise.all(hook.result.data.map(populateSender))
            .then(data => {
              hook.result.data = data;
              resolve(hook);
            }).catch(reject);
        }
        // Handle single objects.
        else {
          populateSender(hook.result).then(message => {
            hook.result.data = message;
            resolve(hook);
          }).catch(reject);
        }
      });
    }
  };
};

Bundled hooks should support a scope

If you manipulated your result object so that the property you want to remove is not at the root level or inside result.data as an array then it won't be removed. This is the case with auth. We set the response to be:

{
  token: 'token',
  data: {
    email: '[email protected]',
    password: 'password'
  }
}

The password won't be removed when using the current remove hook. Ideally, the response from auth should be:

{
  token: 'token',
  user: {
    email: '[email protected]',
    password: 'password'
  }
}

but because we cannot scope the fields to be removed they still won't be using the remove hook. The same goes for the other hooks that manipulate data.

Multi populate with client control of which fields to populate

Here's a populate hook I came up with to populate multiple fields and enable the client to skip populates when not needed.

exports.populate = function (options) {
  /**
   * Example options:
   * messageService.after({
   *   find: [
   *     globalHooks.populate({
   *       user: { // Destination key
   *         service: 'users', // Foreign service
   *         f_key: 'id',  // Foreign key
   *         one: true // Optional, if only one resolve object is wanted
   *       },
   *       comments: { // Destination key
   *         service: 'comments', // Foreign service
   *         f_key: 'message',  // Foreign key
   *         l_key: 'id',  // Local key
   *        },
   *       resolvedCategories: { // Destination key
   *         service: 'categories', // Foreign service
   *         f_key: 'id',  // Foreign key
   *         l_key: 'categories' // Local key, optional, if different then destination key
   *       }
   *     })
   *   ]
   * })
   **/   

  return function(hook) {

    /** 
     * Block some populates from the client:
     * set query.$populate to {dontPopulateField: 0}; 
     * or 
     * set query.$populate to false to block all populates
     **/
    let skip = {};
    if (hook.params.query.$populate || hook.params.query.$populate===false) {
      if (hook.params.query.$populate.constructor === Object) {
        Object.keys(hook.params.query.$populate).map(function(key) {
          if (!hook.params.query.$populate[key]) {
            skip[key] = true;
          }
        });
      }
      let skipAll = hook.params.query.$populate===false;
      delete hook.params.query.$populate;
      if (skipAll) return hook;     
    }

   let populate = function(obj) {
      let populateField = function(key) {
        if (skip[key]) return;
        let option = options[key];
        let find = {
          query: {}
        };
        let value = obj[option.l_key||key];
        if (Array.isArray(value)) value = {$in: value};
        find.query[option.f_key] = value;
        return hook.app.service(option.service).find(find).
          then(result => {
            let data = result.data;
            if (option.one) data = result.data[0];
            obj[key] = data;
            return;
          });
      };
      return Promise.all(Object.keys(options).map(populateField));
    };

    if (hook.result) {
      if (hook.method === 'find' || Array.isArray(hook.result.data)) {
        return Promise.all((hook.result.data || hook.result).map(populate))
          .then(() => {
            return hook;
          });
      } else {
        return populate(hook.result)
          .then(() => {
            return hook;
          });
      }
    } 
    return hook;
  };    
};  

The idea for a new hook

I have an idea for a new hook. Quite often it is necessary to validate of modify fields from request.body.

We can use string package for string validation and transformation .

It provides such methods as:

  • camelize()
S('data_rate').camelize().s; //'dataRate' 
S('background-color').camelize().s; //'backgroundColor' 
S('-moz-something').camelize().s; //'MozSomething' 
S('_car_speed_').camelize().s; //'CarSpeed' 
S('yes_we_can').camelize().s; //'yesWeCan' 
  • capitalize()
S('jon').capitalize().s; //'Jon' 
S('JP').capitalize().s; //'Jp' 
  • escapeHTML()
S('<div>hi</div>').escapeHTML().s; //&lt;div&gt;hi&lt;/div&gt; 
  • slugify()
S('Global Thermonuclear Warfare').slugify().s // 'global-thermonuclear-warfare' 
S('Crème brûlée').slugify().s // 'creme-brulee' 
  • and many other...

As a bonus - we have the methods for validation of the box.

  • isAlpha()
S("afaf").isAlpha(); //true 
S('fdafaf3').isAlpha(); //false 
S('dfdf--dfd').isAlpha(); //false 
  • isAlphaNumeric()
S("afaf35353afaf").isAlphaNumeric(); //true 
S("FFFF99fff").isAlphaNumeric(); //true 
S("99").isAlphaNumeric(); //true 
S("afff").isAlphaNumeric(); //true 
S("Infinity").isAlphaNumeric(); //true 
S("-Infinity").isAlphaNumeric(); //false 
S("-33").isAlphaNumeric(); //false 
S("aaff..").isAlphaNumeric(); //false 
  • isEmpty()
S(' ').isEmpty(); //true 
S('\t\t\t    ').isEmpty(); //true 
S('\n\n ').isEmpty(); //true 
S('helo').isEmpty(); //false 
S(null).isEmpty(); //true 
S(undefined).isEmpty(); //true 

It can looks like:

app.service('users').before({
  create: [
    hooks.transform({
      // The most frequently used transformations can provide of the box.
      email: {
        trim: true,
        lowerCase: true,
        // validations too
        isAlphaNumeric: true,
        isEmpty: false        
      },
      // Custom transformation can be implemented using the function
      username: (v) => v.toLowerCase(),      // sync transformation
      password: (v) => Promise.resolve(v)    // async transformation
    })
  ]
})

I think I can try to implement this functionality. This will solve the problem of validation and transformation of data once and for all :)

Using a hook.disable() on a service should remove that from the Allow header for REST services

I disabled a bunch of service calls using hooks.disable() in a before hook call with code like:


exports.before = {
    all: [],
    find: [],
    get: [],
    create: [hooks.disable()],
    update: [hooks.disable()],
    patch: [hooks.disable()],
    remove: [hooks.disable()]
};

Yet the Allow header still shows all services available:

Allow →GET,POST,PUT,PATCH,DELETE

(From Postman)

I would think that if we use a stock hook.disable() then the associated Allow header component should be removed as well.

normalize parameters

I've been playing around with feathers-hooks and I've been trying to find a way to reuse code. As it's easier to understand with an example, consider the following code:

UsersService.prototype.after = {

    create : function (data, requestData, params, callback) {
        delete data.password;
        callback(null, data, requestData, params);
    },

    find : function (data, params, callback) {
        var filtered = _.each(data, function (user) {
            delete user.password;
        });

        callback(null, filtered, params);
    },

    get : function (data, id, params, callback) {
        delete data.password;
        callback(null, data, id, params);
    },

    update : function (data, id, requestData, params, callback) {
        delete data.password;
        callback(null, data, id, requestData, params);
    },

    patch : function (data, id, requestData, params, callback) {
        delete data.password;
        callback(null, data, id, requestData, params);
    },

    remove : function (data, id, params, callback) {
        delete data.password;
        callback(null, data, id, params);
    }

};

What I would like to do would be something like this:

UsersService.prototype.after = {

    create : deletePassword,

    find : function (data, params, callback) {
        var filtered = _.each(data, function (user) {
            delete user.password;
        });

        callback(null, filtered, params);
    },

    get : deletePassword,
    update : deletePassword,
    patch : deletePassword,
    remove : deletePassword

};

The only way to achieve this is for the deletePassword function to check the number of arguments and act accordingly, which makes the intent less clear in my eyes. Would it be feasible to normalize the parameters to something like the following?

UserService.prototype.after = {
    create : function (obj, callback) {
        console.log(obj);
        // => { data : { ... }, id : ..., requestData : { ... }, params : { ... }  }
        callback(null, obj, callback);
    }
};

// or

UserService.prototype.after = {
    create : function (data, obj, params, callback) {
        console.log(obj);
        // => { id : ..., requestData : { ... } }
        callback(null, data, obj, params, callback);
    }
};

Before all and after all hooks

The hooks plugin should provide the ability to also take just a single hook function (instead of an object) which will be applied to all service methods:

app.service('todos').before(function(hook,next) {
  if(!hooks.params.user) {
    return next(new Error('You need to be authenticated'));
  }
  next();
});

Additionally it could be very helpful to add general hooks to the application which will be applied to every service (this could be easiliy implemented as a service mixin):

app.before({
  create: function(hook, next) {
    next();
  }
});

Customize existing hook - 'this' undefined - TypeError

I'm trying to add features to an existing hook. I'm following the last example from HERE. I get a 'TypeError can't call a method of undefined'.

I have debugged and managed to find that 'this' is undefined inside auth.restrictToOwner(). 'this' is defined in my hookWrapper scope. What is the cause of this?

My code is below

const auth = require('feathers-authentication').hooks;

exports.hookWrapper = function(options){
    // 'this' is populated
    return function(hook){
    // 'this' is populated

        return new Promise(function(resolve, reject){
            //call restrictToOwner()
            auth.restrictToOwner(options)(hook).then(function(hook){
                resolve(hook);
            }).catch(function(error){
                // I get error here 
                // TypeError can't call method 'get' of undefined

                reject(error);
            });

        });
    };
};


//In my service I call it like this
const globalHooks = require('../../../hooks');

exports.before = {
    get : [
        globalHooks.hookWrapper()
    ]
};

Hooks cannot remove inherited properties

I use feathers-mongoose and try to remove password field with after hook but it doesn't work.

I found that it's caused by containsField() and removeField() method that use hasOwnProperty() to check for property existence. However, properties in an object from feathers-mongoose are inherited properties which hasOwnProperty() will return false.

I will make an PR to solve this issue by searching through the prototype chain to remove inherited properties.

Sanitation hooks for each db type

From @ekryski's feathersjs-ecosystem/authentication#132 (comment)
See more comments at feathersjs/feathers#280 (comment)

One thing we may want to do though, is have a built in hook that runs on every POST, PATCH, PUT request and escapes script code rather than relying on making sure the ORM or the developer handle it.
We ought to provide this for at least the db adapters that don't have an ORM that does it already. I'm pretty sure Mongoose takes care of this, but we ought to check everything.

Must be explicitly added.
Each DB type will need its own sanitation hook.

Hooks are always called even if you don't want them to be

So I have a scenario like this:

// In user service

// ....
var Service = require('feathers-mongoose').Service;
var ensureAuthenticated = function(hook, next) {
  if (hook.params.user.authenticated) {
    return next();
  }

  next(new app.errors.NotAuthenticated());
};

var UserService = Service.extend({
  before: {
    find: [ensureAuthenticated]
  },
});

// ....

// In auth.js
app.post('/login', function(req, res, next){
  var query = {
    email: '[email protected]'
  };

app.post('/login', function(req, res, next){
  var query = {
    email: '[email protected]'
  };

  userService.find({ query: query }, function(err, users){
    if (err) {
      return next(err);
    }

    if (!users || !users.length) {
      return next(new app.errors.NotFound('Invalid Email'));
    }

    if (users[0].password !== req.body.password) {
      return next(new app.errors.BadRequest('Invalid Password'));
    }

    res.json(users);
  });
});

The before hook fires when I do GET /users (which is great) however, it also fires in the userService.find() call. I don't want this, as I am trying to authenticate and need to do it by looking up the user in the db.

@daffl any suggestions on how to solve this problem would be gladly welcome. 😄

Populate hook doesn't work with populating arrays

Say I have a schema, Group defined as

const groupSchema = new Schema({
  created: { type: Date, default: Date.now },
  updated: { type: Date, default: Date.now },
  posts: [{ type: Schema.Types.ObjectId, ref: 'Post' }]
});

that has a list for Post objects for a relationship. Putting in a populate hook:

groupService.after([
  hooks.populate('posts', {
    service: '/posts'
  })
]);

This hook should query the posts service for every id in that list. But if you look here, in bundled.js:

function populate(item) {
      if (!item[field]) {
        return Promise.resolve(item);
      }
      // `item` will be a group object
      var id = item[field];
      // `id` now is the lists of post ids
      if (typeof item.toObject === 'function') {
        item = item.toObject(options);
      }
      else if (typeof item.toJSON === 'function') {
          item = item.toJSON(options);
        }
       // passing a list of posts ids
      return hook.app.service(options.service).get(id, hook.params).then(function (relatedItem) {
        if (relatedItem) {
          item[target] = relatedItem;
        }

        return item;
      }).catch(function () {
        return item;
      });
    }

the hook.app.service(options.service).get(id, hook.params) line is getting passed an array of relationship objects, not a single id.

I can make a pull request fixing this if if you all concur (it's late, not sure if bug or netflix hangover).

Unable to modify result object in after hook

In the README, it states "In any after hook, only modifications to the result object will have any effect". I've noticed that when I do something like hook.result = {}, the data sent back to the client matches {} but when I do hook.result.token = jwt.sign..., not only does hook.result.token not print to the console with console.log, but the token never shows up in my response data. The same is true when I try delete hook.result.password, the password shows up in the final response.

In order to add a token, would I have to copy all the keys and values that I want to include into a new object and then add the new ones? Like so: hook.result = {"id": "someID", "Token":"some_token"}.

If that is the case, is it possible to change that? Being able to do hook.result.some_property = ... wouldn't break the invariant of "only modifications to the result object will have any effect" and it would help to keep the code inside hook functions to a bare minimum.

Throw an error for unknown hook methods

As seen in #117 (comment) it can be easy to accidentally register hooks the wrong way. It already throws an error when registering an unknown hook type but also should error when a method hook is added that is not a service method.

examples not working

Hi thanks for feathers, first of all, just discovered it a few days ago. great work.
I'm trying to get hooks working, but no luck for me.
I cloned the rep and tried to run the examples, but i always get:

var todoService = app.lookup('todos');
TypeError: undefined is not a function

any idea what could be happening there? I'm on ubuntu 14.04 and node 0.12.7.

more request param in hook?

Hello,
I want to filter the accessibility with the request IP address. But I find the hook parameter seems not carry req.ip with it. My workaround is to use a middleware. Will it be better if hook contains more information?

Create a "select" or "pluck" hook for serialization

If you have a large object it is a pain in the ass to remove a bunch of fields when you really only want to pluck a handful from your returned object.

By chaining pluck and remove hooks you can create really powerful serializers.

Allow hooks to return a promise

I want to be able to return a promise from a hook. Promise return values should not be used so that we can do things like:

userService.before({
    remove: function(hook) {
        // Delete the user from intercom.io first
        return Q.ninvoke(intercom, 'deleteUser', { user_id: hook.id });
    }
});

If you want to modify hook data, just change it after .then:

userService.before({
  remove: function(hook) {
    // Delete the user from intercom.io first
    return Q.ninvoke(intercom, 'deleteUser', { user_id: hook.id }).then(function(user) {
      hook.id = user.id;
    });
  }
});

Errors in the promise will be treated as actual errors.

Be able to apply hooks to custom service methods

I'd like to be able to have a service like so:

var myCustomHook = function(hook, next){
  // do something custom
  next();
};

var CustomService = Service.extend({
    before: {
      customMethod: [myCustomHook]
    },

    customMethod: function(params, cb){
      // Do some custom stuff
      cb();
    }
});

Currently hooks only fire on our default service methods.

How to mix hooks together (pluck + populate)

Imagine you have user objects and company objects. Company contains a reference to a user.

What's the better way to achieve mixing hooks like calling the "pluck" hook inside a "populate" hook to make the populate of company's get method sending a result containing only few properties of user instead the full user object ?

I can't use user after hook because my user get method called out the company context should return all the user fields.

I achieved that with a new service based on the same model as user and implementing only the get method with a "pluck" after hook. The problem is that it leads on multiple services used only to filter data which is messy.

Hooks with mutiple entries per request

There should be definitely some change in either documentation or in way how hooks works in case of multiple entries per request (eg. create([entry, entry, ...])).

Built-in hooks works just fine, when you call hooks.remove('id'), it runs for every entry. However, when you want use hook => { delete hook.data.id; }, you accomplish nothing. Because hook.data contains array of entries.

This leaves possible security breach in a lot of applications that sanitizes hook data by hand (=not-built-in hooks).

From my point of view, there are two possible solutions:

  1. Change documentation of hooks in way that users are aware of this problem
  2. Change behavior of hooks so hook will run for every entry, as if they would run separately and place complete data for example to hook.multipleData

Second solution is IMO better, because it will increase security of most apps with no cost. It will break some older apps, but it will be quick fix since everything you have to do is change your hook code from hook.data to hook.multipleData (in case that your app needs to work with entire collection).

afterError hooks

Currently only successful after hooks will run and there is no way to post-process errors. We can add an afterError handler that works the same as the other hooks where you can process errors (e.g. to translate the message or turn it into an entirely new error):

app.service('messages').onError({
  create(hook) {
    hook.error
  }
});

An important difference is that afterError hooks will always end in a rejected promise so it is not possible to use those hooks to turn the method call into a successful promise instead.

should be able to bypass remove hook

Inside feathers-authentication we lookup the user in order to compare their password. If someone has an remove('password') as an after hook no one can log in. So we need a way to be able to conditionally bypass the remove hook.

no hook result when internal service call

Hi I have service A and service B.
Service B calls service A via app.services.
Service A has a after hook on GET, which does get executed.
The problem is that the result I get isn't the result from the after hook, but when you normally do a fetch with mongoose.

Is this a bug?

Remove ref doc will broke the parent router

let's have service A and service B
when A has many B

A: { childrenIds: [1, 2, 3] }

then you use populate hook on A like

const findPatents = hooks.populate('children', {
  service: 'B',
  field: 'childrenIds'
})

you remove any B will cause A router "404 not found"

is this consider a bug ?

Remove field and haspassword hooks are not working properly.

@ekryski I am trying to use CLI generated user service on the server. Default user service is using remove(password) and auth.hashPassword() hooks. When I try to create a new user on the server, the returned newly created user object still has password field. The second issue is with hashPassword hook I am getting password validation error., where as If I remove haskPassword hook then user is created without any error. Here is my user model and code to create a new user on server.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const loginMatch = [ /^[a-zA-Z0-9]{6,20}$/, 'Login name must be 6 to 20 characters long, and can contain A-Z, a-z and 0-9.'];
const passwordMatch = [ /^(?=.*[A-Z])(?=.*[0-9])(?=.{6,15}$)/, 'Password must be 6 to 15 characters long, and contain at least 1 uppercase letter and 1 digit.'];

const userSchema = new Schema({

  userName: { type: String, required: true, maxlength: 25 },
  role: { type: String, required: true, default: 'Customer' },
  belongTo: { type: Schema.Types.ObjectId, default: null },
  isActive: { type: Boolean, required: true, default: true },

  loginName: {type: String, required: true, unique: true, match: loginMatch},
  password: { type: String, required: true, match: passwordMatch },

  createdAt: { type: Date, 'default': Date.now },
  updatedAt: { type: Date, 'default': Date.now }
});

const userModel = mongoose.model('user', userSchema);

module.exports = userModel;
   app.service('users').create({
                userName: 'Zafar Ansari',
                role: 'Admin',
                loginName: 'zafaransari',
                password: 'Billgates3'
            }).then(function (user) {
                console.log('Created user', user);
            }).catch(function(error){
                console.log(error);
            });

Should support promises returned from service methods

Fairly important for Feathers 2. The way Hooks currently work and due to the async processing in the overwritten methods, Promises originally returned from a service will be swallowed by a before and after hook and the callback will be used instead (this will al ways work because of Feathers promise normalization). We should instead migrate Hooks to be completely promise compatible which will also make for nicer synchronous APIs without needing next like:

app.service('todos').before({
  create(hook) {
    if(!hook.data.text) {
      throw new Error('Text has to be provided');
    }

    hook.data.text = data.text.substring(0, 255);
  }
});

Pop wrong hooks in bundled.test

In bundled test, some pops are wrong and affect other tests.

Line 174 - 175 must pop get not find. So, they affect line 254-265 to contain result.data from the un-poped get-afterhook.

Line 258-259 should pop get once instead of two finds.

And the last test should pop find-afterhook to prevent its side-effect to other following tests in the future.

I'll make a PR to solve these pop issues.

Throw or log an error for catch block in populate method.

In this bit of code (bundled.js) :

// If the relationship is an array of ids, fetch and resolve an object for each, otherwise just fetch the object.
var promise = Array.isArray(id) ? Promise.all(id.map(function (objectID) {
  return hook.app.service(options.service).get(objectID, hook.params);
})) : hook.app.service(options.service).get(id, hook.params);
return promise.then(function (relatedItem) {
  if (relatedItem) {
    item[target] = relatedItem;
  }
  return item;
}).catch(function () {
  return item;
});

the catch function doesn't give any indication that something went wrong. I spent several hours debugging this to finally figure out that it was my auth.restrictToOwner({ ownerField: '_id' }) hook which was causing the populate method not to work (since the promise was rejected). It just kept returning the original result and I couldn't figure out why it wasn't working.

Would it be possible to log or throw an error in the catch here? Or is there another way I should have known what was happening?

Thanks!

Arrays of Hooks are running in reverse order.

When setting up hooks like this:

service.before({
  find: [hooks.requireAuth, hooks.setOwner]
});

... the hooks are getting executed in reverse order, so setOwner in this case is running before requireAuth. In my opinion, they should be running in the order listed in the array.

I'm running feathers-hooks version 0.4.0 on top of feathers 1.0.0.

Ability to call the same service from a hook?

Hi guys,

I have a quick question, probably you've got the answer.

I have a message service defined. When I create a message, I'd like to automatically create a new message. I thought it would be a good idea to do that in the after hook. Doesn't work.

Here's my code:

export default function() {
  return function(hook) {
    if (!hook.result.message.robot) {
      console.log('conversationID: ' + hook.result.message.conversationId);
      return hook.app.service('/api/v1/messages').create({
        message: {
            body: 'Hold on, let me check what I can do',
            robot: true,
            conversationId: hook.result.message.conversationId
        }
      }).then(result => {
         console.log("Robot answer");
      });
    }
  };
}

Any ideas?

Thanks in advance.

hook on non-service apps?

I want to authenticate the user before the request
app.post('/import', import)
which is not a feather service.

Is it possible to use the hook

import.before({
  [
    authHooks.verifyToken({secret: 'feathers-rocks'}),
    authHooks.populateUser(),
    authHooks.requireAuth()
  ]
})

instead of writing the middle-ware by myself?

feathers-hooks is changing the scope of makeWrapper()

When using feathers-hooks, the scope of the makeWrapper() function is the global node object. Since that same scope is passed to the feathers-mongodb service, it causes an error here of TypeError: Cannot call method 'find' of undefined. (this.collection is undefined on the global node object).

If I comment out the users.before() line in the example code below, the scope of this inside makeWrapper() is the service with its methods.

I've tested both the latest commit of feathers-hooks and the 0.4.0 version from npm.

So far this has only been a problem when using feathers-mongodb because of its use of this inside the methods.

Here is the simple server.js I'm using to test:

var feathers = require('feathers'),
  fHooks = require('feathers-hooks'),
  mongo = require('mongoskin'),
  fMongo = require('feathers-mongodb'),
    bodyParser = require('body-parser');

var app = feathers()
    .use(feathers.static(__dirname + '/public'))
    .configure(feathers.rest())
    .use(bodyParser())
    .configure(fHooks())
    .configure(feathers.socketio())
    .configure(feathers.errors());

// Synchronous connect to db
var db = mongo.db('mongodb://localhost:27017/feathers-tuts');
var userService = fMongo({collection:db.collection('users')});

// The users endpoint must match the one setup on config.store.
app.use('api/users', userService);
var users = app.service('api/users');
// Dummy hook
function hookMe(hook, next){
    next();
}
users.before({find:[hookMe]});

// Start the server.
var port = 8080;
app.listen(port, function() {
  console.log('Feathers server listening on port ' + port);
});

multiple hooks / array of hooks

How about arrays as another variant to declare hooks? It might be obvious that I'm coming from a SailsJS background, but I'm quite fond of the way policies work.

UserService.prototype.before = {
  create : function (data, params, callback) {    
    async.applyEach([isAuthenticated, hasPermissions], data, params, callback);
  }
};

Would be equivalent to:

UserService.prototype.before = {
  create : [isAuthenticated, hasPermissions]
};

For reference: async#applyEach

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.