A remote method is a static method of a model, exposed over a custom REST endpoint.
Page Contents

Overview

A remote method is a static method of a model, exposed over a custom REST endpoint. Use a remote method to perform operations not provided by LoopBack’s standard model REST API.

How to define a remote method

To define a remote method:

  1. Edit the Model definition JSON file in /common/models directory; for example, to attach a remote method to the Person model, edit /common/models/person.js. If you created the model with the Model generator, then this file will already exist.

  2. Define a static method that will handle the request.

  3. Call remoteMethod(), to register the method, calling it with two parameters: 

    • First parameter is a string that is the name of the method you defined in step 2 
    • Second (optional) parameter provides additional configuration for the REST endpoint.

 

Example

See additional introductory examples in Extend your API.

Suppose you have a Person model and you want to add a REST endpoint at /greet that returns a greeting with a name provided in the request. You add this code to /common/models/person.js:

/common/models/person.js

module.exports = function(Person){

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

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

Now, for example, a request to

POST /api/people/greet

with data {"msg": "John"}

will return:

shell

Greetings... John!

Using async/await

Remote methods can also return a promise instead of using the callback parameter.

/common/models/person.js

module.exports = function(Person){

    Person.greet = async function(msg) {
        return 'Greetings... ' + msg;
    }

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

Registering a remote method

All LoopBack models have a remoteMethod() static method that you use to register a remote method:

model.remoteMethod(requestHandlerFunctionName, [options])

Where:

  • model is the model object to which you’re adding the remote method. In our example, Person.
  • requestHandlerFunctionName is a string that specifies name of the remote method, for example 'greet'.
  • options is an object that specifies parameters to configure the REST endpoint; see below.

Options

The options argument is a Javascript object containing key/value pairs to configure the remote method REST endpoint.

Option Description Example
accepts

Defines arguments that the remote method accepts. These arguments map to the static method you define. For the example above, you can see the function signature:

Person.greet(name, age, callback)...

`name` is the first argument, `age` is the second argument and callback is automatically provided by LoopBack (do not specify it in your `accepts` array). For more info, see Argument descriptions.

Default if not provided is the empty array, [].

{  ...
  accepts: [
   {arg: 'name', type: 'string'},
   {arg: 'age', type: 'number'},...],
  ... }
description

Text description of the method, used by API documentation generators such as Swagger.

You can put long strings in an array if needed (see note below).

http.path

HTTP path (relative to the model) at which the method is exposed.

http: {path: '/sayhi'}
http.verb

HTTP method (verb) at which the method is available. One of:

  • get
  • post (default)
  • patch
  • put
  • del
  • all
http: {path: '/sayhi', verb: 'get'}
http.status Default HTTP status set when the callback is called without an error.
http: {status: 201}
http.errorStatus Default HTTP status set when the callback is called with an error.
http: {errorStatus: 400}
isStatic Boolean. Whether the method is static (for example MyModel.myMethod). Use false to define the method on the prototype (for example, MyModel.prototype.myMethod). Default is true.
notes

Additional notes, used by API documentation generators like Swagger.

You can put long strings in an array if needed (see note below).

returns

Describes the remote method's callback arguments; See Argument descriptions. The err argument is assumed; do not specify.

Default if not provided is the empty array, [].

returns: {arg: 'greeting', type: 'string'}

Argument descriptions

The accepts and returns options properties define either a single argument as an object or an ordered set of arguments as an array. The following table describes the properties of each individual argument.

Property (key) Type Description
arg String Argument name
description String or Array

A text description of the argument. This is used by API documentation generators like Swagger.

You can split long descriptions into arrays of strings (lines) to keep line lengths manageable.

[
"Lorem ipsum dolor sit amet, consectetur adipiscing elit,"
"sed do eiusmod tempor incididunt ut labore et dolore",
"magna aliqua."
]
http Object or Function For input arguments: a function or an object describing mapping from HTTP request to the argument value. See HTTP mapping of input arguments below.
http.target String

Map the callback argument value to the HTTP response object. The following values are supported.

  • status sets the res.statusCode to the provided value
  • header sets the http.header or arg named header to the value
required Boolean True if argument is required; false otherwise.
root Boolean For callback arguments: set this property to true if your function has a single callback argument to use as the root object returned to remote caller. Otherwise the root object returned is a map (argument-name to argument-value).
type String Argument datatype; must be a Loopback type. Additionally, callback arguments allow a special type "file"; see below.
default String Default value that will be used to populate loopback-explorer input fields and swagger documentation. Note: This value will not be passed into remote methods function if argument is not present.

For example, a single argument, specified as an object:

{arg: 'myArg', type: 'number'}

Multiple arguments, specified as an array:

[
  {arg: 'arg1', type: 'number', required: true},
  {arg: 'arg2', type: 'array'}
]

Returning a file (stream) response

You can specify { type: 'file', root: true } for a callback argument that will be sent directly as the response body. A file argument can be set to one of the following values:

Example:

module.exports = function(MyModel) {
  MyModel.download = function(cb) {
    // getTheStreamBody() can be implemented by calling http.request() or fs.readFile() for example
    getTheStreamBody(function(err, stream) {
      if (err) return cb(err);
      // stream can be any of: string, buffer, ReadableStream (e.g. http.IncomingMessage)
      cb(null, stream, 'application/octet-stream');
    });
  };

  MyModel.remoteMethod('download', {
    isStatic: true,
    returns: [
      {arg: 'body', type: 'file', root: true},
      {arg: 'Content-Type', type: 'string', http: { target: 'header' }}
    ]
  });
};

HTTP mapping of input arguments

There are two ways to specify HTTP mapping for input parameters (what the method accepts):

  • Provide an object with a source property
  • Specify a custom mapping function

Using an object with a source property

To use the first way to specify HTTP mapping for input parameters, provide an object with a source property that has one of the values shown in the following table.

Value of source property Description
body The whole request body is used as the value.
form
query
path

The value is looked up using req.param, which searches route arguments, the request body and the query string.

Note that query and path are aliases for form.

req The Express HTTP request object.
res The Express HTTP response object.
context The whole context object, which holds request and response objects.

For example, an argument getting the whole request body as the value:

{ arg: 'data', type: 'object', http: { source: 'body' } }

Another example showing the Express HTTP request and response objects:

[
 {arg: 'req', type: 'object', 'http': {source: 'req'}},
 {arg: 'res', type: 'object', 'http': {source: 'res'}}
]

Using a custom mapping function

The second way to specify HTTP mapping for input parameters is to specify a custom mapping function; for example:

{
  arg: 'custom',
  type: 'number',
  http: function(ctx) {
    // ctx is LoopBack Context object

    // 1\. Get the HTTP request object as provided by Express
    var req = ctx.req;

    // 2\. Get 'a' and 'b' from query string or form data and return their sum.
    return -req.param('a') - req.param('b');
  }
}

If you don’t specify a mapping, LoopBack will determine the value as follows (assuming name as the name of the input parameter to resolve):

  1. If there is an HTTP request parameter args with JSON content, then it uses the value of args['name'].
  2. Otherwise, it uses req.param('name').

Returning data outside of a JSON field

Specifying a return argument with the arg property will automatically return a JSON object with your data stored within a field of the same name.

If you want to return data as the main response, for example an array, you can do so by setting the root property within the returns object and omitting arg

returns: {type: 'array', root: true}

Setting a remote method route

By default, a remote method is exposed at:

POST http://apiRoot/modelName/methodName

Where

  • apiRoot is the application API root path.
  • modelName is the plural name of the model.
  • methodName is the function name.

Following the above example, then by default the remote method is exposed at:

POST /api/people/greet

To change the route, use the http.path and http.verb properties of the options argument to remoteMethod(), for example:

/common/models/model.js

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

This call changes the default route to 

GET /api/people/sayhi

So a GET request to http://localhost:3000/api/people/sayhi?msg=LoopBack%20developer returns:

{"greeting": "Greetings... LoopBack developer"}

Non-public Information

Extending a model

Add default functions for properties

<div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>common/models/order.js</b></div>
<pre class="theme: Emacs; brush: js; gutter: false" style="font-size:12px;">module.exports = function(Order) {   Order.definition.rawProperties.created.default = function() {
return new Date();   };   Order.definition.rebuild(true); }</pre></div>

Add custom methods

<div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>common/models/customer.js</b></div>
<pre class="theme: Emacs; brush: js; gutter: false" style="font-size:12px;">module.exports = function(Customer) {
Customer.prototype.getFullName = function() {
  return this.firstName - ' ' - this.lastName;
};
Customer.listVips = function(cb) {
  this.find({where: {vip: true}}, cb);
}
} </pre></div>

</div> </div>

Adding ACLs to remote methods

To constrain access to custom remote methods, use the ACL generator in the same way you control access to any model API. The access type for custom remote methods is Execute.

Basic use

For example, to deny invocation of the greet method used in the examples above:

shell

$ slc loopback:acl
[?] Select the model to apply the ACL entry to: Person
[?] Select the ACL scope: A single method
[?] Enter the method name: greet
[?] Select the access type: Execute
[?] Select the role: All users
[?] Select the permission to apply: Explicitly deny access

shell

$ apic loopback:acl
[?] Select the model to apply the ACL entry to: Person
[?] Select the ACL scope: A single method
[?] Enter the method name: greet
[?] Select the access type: Execute
[?] Select the role: All users
[?] Select the permission to apply: Explicitly deny access

The tool then creates the following access control specification:

/common/models/person.json

...
"acls": [{
  "principalType": "ROLE",
  "principalId": "$everyone",  // apply the ACL to everyone
  "permission": "DENY",        // DENY attempts to invoke this method
  "property": "greet"          // applies the access control to the greet() method
}],
...

Advanced use

Another example, to allow invocation of the a remote method only for the $owner of that model object:

/common/models/YourModel.js

module.exports = function(YourModel) {
  //...
  YourModel.remoteMethod(
    'someRemoteMethod',
    {
      accepts: [
        {arg: 'id', type: 'number', required: true}
      ],
      // mixing ':id' into the rest url allows $owner to be determined and used for access control
      http: {path: '/:id/some-remote-method', verb: 'get'}
    }
  );
};

Formatting remote method responses

You can reformat the response returned by all remote methods by adding a boot script  that modifies the object returned by app.remotes() as follows:

/server/boot/hook.js

module.exports = function(app) {
  var remotes = app.remotes();
  // modify all returned values
  remotes.after('**', function (ctx, next) {
    ctx.result = {
      data: ctx.result
    };

    next();
  });
};