Loopback: Document how to add remote method to a built-in model

Created on 6 Nov 2014  路  18Comments  路  Source: strongloop/loopback

Currently, we document how to define a remote method for a custom model, and how to create a custom model that extends a built-in model (to which you could attach a remote method) but NOT how to attach a remote method directly to a built-in model.

Summary (example):

  1. Create a boot script, eg. /server/boot/userRemoteMethods.js.
  2. Add the following to this file:
User = app.models.User;
module.exports = function(User){

    User.greet = function(msg, cb) {
      cb(null, 'Greetings... ' + msg);
    }

    User.remoteMethod(
        'greet', 
        {
          accepts: {arg: 'msg', type: 'string'},
          returns: {arg: 'greeting', type: 'string'}
        }
    );
};
doc

Most helpful comment

In mailing list Raymond then mentioned required additional step:

Add the following ACLs to the "User" model in server/model-config.json:

"acls": [
  {
     "principalType": "ROLE",
     "principalId": "$everyone",
     "permission": "ALLOW",
     "property": "greet"
  }
]

All 18 comments

@bajtos do we support extending built in models from a model script? Or is this only possible from a boot script.

do we support extending built in models from a model script?

Not yet - see #397.

Or is this only possible from a boot script.

Yes.


I'd rather postpone this task until we implement redefine:true as proposed in #397:

A better solution is to implement redefine:true. A redefined "User" model is defined in common/models/user.json and scaffolded by yo loopback. Benefits: clean solution, allows redefinition of common models in the server facet.

@bajtos when you say

I'd rather postpone this task ....

Are you referring to documenting how to add remote method to a built-in model (in a boot script, apparently the only way to do so) or support for extending built-in models in a model script?

@crandmck I wanted to say that it's better to wait with documentation until we have implemented support for extending built-in models via a model script. The solution based on boot scripts is kind of hacky.

Although if extending builtin models is something that many people are asking for, then maybe we should not wait. In which case it would be good to mention that the current solution described in docs is not optimal and that we are planning to implement a better solution in the near future.

Here is a corrected boot script:

module.exports = function(app) {
  var User = app.models.User;

  User.greet = function(msg, cb) {
    cb(null, 'Greetings... ' + msg);
  }

  User.remoteMethod(
    'greet', 
    {
      accepts: {arg: 'msg', type: 'string'},
      returns: {arg: 'greeting', type: 'string'}
    }
  );
};

I agree with @bajtos proposal to wait until redefine: true is supported.

Same here.

OK!

So I was looking for exactly this sort of documentation and the funny thing is I found it here in an issue created to document it but then again not document it :)
Given that an implementation isn't ready for the best-case scenario, wouldn't you say its easier on the loopback consumers to learn of this hacky approach?

Or is this only possible from a boot script.

I mean it is no hack~ier than the user vs User modification that I would end up with due to my lack of knowledge about this current workaround ... no?

When I put myself in your shoes though ... as a company, maybe you want to field such Qs in google groups and not in the public docs? _shrug_ ... Is that what's going on?

Does the example really work?

Keep getting 401 Authorization Required when invoking that method, despite known good AccessToken. Seems to be coming from loopback/common/models/roles.js .

What else do I need to do except above code, to make it work?

Did add one line for an HTTP GET for easier testing, so currently I have:

module.exports = function(app) {
  var User = app.models.User;

  User.greet = function(msg, cb) {
    cb(null, 'Greetings... ' + msg);
  }

  User.remoteMethod(
    'greet', 
    {
      accepts: {arg: 'msg', type: 'string'},
      returns: {arg: 'greeting', type: 'string'},
      http: { verb: 'get', path: '/greet' }
    }
  );
};

Then for URL (with valid token)

http://127.0.0.1:3000/api/Users/greet?msg=%22Joe%22&access_token=QqU0tx...

I keep getting

{"error":{"name":"Error","status":401,"message":"Authorization Required","statusCode":401,"stack":"Error: Authorization Required
    at app.enableAuth.isAuthEnabled (/home/joeb/project17/node_modules/loopback/lib/application.js:327:21)
    at Model.checkAccess (/home/joeb/project17/node_modules/loopback/lib/model.js:295:5)
    at module.exports.ACL.checkAccessForContext (/home/joeb/project17/node_modules/loopback/common/models/acl.js:437:23)
    at _asyncMap (/home/joeb/project17/node_modules/loopback/node_modules/async/lib/async.js:254:17)
    at done (/home/joeb/project17/node_modules/loopback/node_modules/async/lib/async.js:135:19)
    at _toString (/home/joeb/project17/node_modules/loopback/node_modules/async/lib/async.js:32:16)
    at _asyncMap (/home/joeb/project17/node_modules/loopback/node_modules/async/lib/async.js:251:21)
    at results (/home/joeb/project17/node_modules/loopback/node_modules/async/lib/async.js:575:34)
    at module.exports.ACL.checkAccessForContext.find.async.parallel.resolved.permission (/home/joeb/project17/node_modules/loopback/common/models/acl.js:420:17)
    at module.exports.Role.isInRole.resolver (/home/joeb/project17/node_modules/loopback/common/models/role.js:219:21)"}}

Just as a test, same server success with GETting a User with a URL

http://127.0.0.1:3000/api/Users/972294...?access_token=QqU0tx...

IIRC you need to add isStatic:true to your remoting metadata.

User.remoteMethod(
    'greet', 
    {
      isStatic: true,
      accepts: {arg: 'msg', type: 'string'},
      returns: {arg: 'greeting', type: 'string'},
      http: { verb: 'get', path: '/greet' }
    }
  );

See also https://github.com/strongloop/loopback/issues/597.

Tried variations without and with isStatic: true, and without and with prototype, and both with User.remoteMethod and with loopback.remoteMethod. No luck.

Please run your app with DEBUG=loopback*,strong-remoting* and see if you can find any hints in the debug logs. If you are still not able to figure this out, then please post a question to our mailing list, so that we don't derail this doc task into a support thread.

In mailing list Raymond then mentioned required additional step:

Add the following ACLs to the "User" model in server/model-config.json:

"acls": [
  {
     "principalType": "ROLE",
     "principalId": "$everyone",
     "permission": "ALLOW",
     "property": "greet"
  }
]

wait until redefine: true is supported.

Since we were not able to implement redefine:true in the last 18 months, I think it will be best to stop waiting and update the docs right now.

/cc @crandmck

I think it will be best to stop waiting and update the docs right now.

I agree.

@bajtos I'd like to assign this (and a few other LB doc issues) to @sequoia (who is doing some contract work for us) but he is not a collaborator in this repo... Can you do something so I can assign issues to him? PM me if you have questions.

Writing up docs for this now. Unless I'm mistaken, it looks like methods added in this way _do not_ show up in the API explorer, unfortunately, so I'll note this in the docs.

Please review: https://docs.strongloop.com/display/APIC/Adding+Remote+Methods+to+Built-In+Models

If it looks good, feel free to remove page restrictions and close this ticket.

Edited and published. Thanks, @Sequoia !

Was this page helpful?
0 / 5 - 0 ratings