Feathers: Adding custom method to service

Created on 19 Jun 2014  路  9Comments  路  Source: feathersjs/feathers

Hi,
How can I add custom method to service like "count" method? or as loopback does ( http://docs.strongloop.com/display/DOC/Exposing+models+over+a+REST+API ).

Thanks

Most helpful comment

Having a method like that is not very RESTful (I'd probably try to use the HTTP range header) but it is still easy to add as a custom middleware in the services .setup:

var MyService = {
  items: [],
  find: function(params, callback) {
    callback(null, this.items)
  },
  count: function(callback) {
    callback(null, {
      total: this.items.length
    });
  },
  // ...
  setup: function(app, path) {
    var self = this;

    app.get(path + '/count', function(req, res) {
      self.count(function(error, data) {
        res.end(data);
      });
    });
  }
}

Another option would be for .find to return an object instead of an array that contains counting information and add a params flag that allows you to only retrieve that:

var MyService = {
  items: [],
  find: function(params, callback) {
    var info = {
      from: 0,
      to: items.length,
      total: items.length
    };

    if(!params.query.count) {
      info.data = this.items;
    }

    callback(null, info);
  }
}

All 9 comments

Having a method like that is not very RESTful (I'd probably try to use the HTTP range header) but it is still easy to add as a custom middleware in the services .setup:

var MyService = {
  items: [],
  find: function(params, callback) {
    callback(null, this.items)
  },
  count: function(callback) {
    callback(null, {
      total: this.items.length
    });
  },
  // ...
  setup: function(app, path) {
    var self = this;

    app.get(path + '/count', function(req, res) {
      self.count(function(error, data) {
        res.end(data);
      });
    });
  }
}

Another option would be for .find to return an object instead of an array that contains counting information and add a params flag that allows you to only retrieve that:

var MyService = {
  items: [],
  find: function(params, callback) {
    var info = {
      from: 0,
      to: items.length,
      total: items.length
    };

    if(!params.query.count) {
      info.data = this.items;
    }

    callback(null, info);
  }
}

Thanks a lot.

Simply use the app.get(path + '/count', function(req, res) doesn't seem to work, it'll still route to the service get method, also if you add additional path under the service path, it won't work either.

The following code doesn't work for me

app.route('/user/auth/signin').post(usersCtrl.signin);
app.route('/user/signin').post(usersCtrl.signin);

it get to work when I change the path to:

app.route('/auth/signin').post(usersCtrl.signin);

Can anyone answer what is behind the scenes?

Odd, I think this used to work before switching to Express 4 (v0.4.x). Internally it just sets up a router for the service at https://github.com/feathersjs/feathers/blob/master/lib/providers/rest/index.js#L40. It seems like when setting up a router like that you can't add any other routes. I don't think for services it makes a difference if we are using a router or individual routes so we should probably change it back to individual routes so that this is still possible.

@daffl What was the issue here? I didn't quite follow. It's not routing quite properly when you define a resource?

Maybe app.route isn't quite the right thing there. Maybe we should be using router.use() instead. http://expressjs.com/4x/api.html#router

Yes, it does not seem to work in the service setup. I think the problem is route duplication. get('/app/:id') route is overriding my new get('/app/count') route.
I tried to use app.param with regex using this but it does not work in service setup.
Only it works when you can define count route before the todos service initialization.

var feathers = require('../lib/feathers');
var TodoService = require('./todos');

var app=feathers();
app.param(function(name, fn){
  if (fn instanceof RegExp) {
    return function(req, res, next, val){
      var captures;
      if (captures = fn.exec(String(val))) {
        req.params[name] = captures;
        next();
      } else {

        next('route');
      }
    }
  }
});
app.param('id', /^\d+$/);
app.get('/todos/count', function(req, res) {
      TodoService.count(function(error, data) {
        res.end(JSON.stringify(data));
      });
});

app.configure(feathers.rest())
  .use(feathers.static(__dirname))
  .use('/todos', TodoService)
  .listen(8080);

That's what I thought. It's because the :id route gets hit before the other one. The solution is probably registering the service REST routes after service.setup is called on app.setup.

I'm working on a solution for this. Hope to have a PR ready for review tomorrow.

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue with a link to this issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

perminder-klair picture perminder-klair  路  3Comments

arve0 picture arve0  路  4Comments

NetOperatorWibby picture NetOperatorWibby  路  4Comments

arkenstan picture arkenstan  路  3Comments

stephane303 picture stephane303  路  3Comments