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.
Note: The easiest way to define a remote method is by using the command-line remote method generator.
For an introductory example of defining a remote method, see Extend your API in Getting Started.
How to define a remote method
To define a remote method:
-
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. -
Define a static method that will handle the request.
-
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.
Important:
The LoopBack model generator automatically converts camel-case model names (for example MyModel) to lowercase dashed names (my-model).
For example, if you create a model named “FooBar” with the model generator, it creates files foo-bar.json
and foo-bar.js
in common/models
.
However, the model name (“FooBar”) will be preserved via the model’s name property.
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
:
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!
Note:
Notice the REST API request above uses the plural form “people” instead of “person”. LoopBack exposes the plural form of model names for REST API routes.
Using async/await
Remote methods can also return a promise instead of using the callback parameter.
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.
Important:
All of the options properties are optional. However, if the remote method requires arguments, you must specify accepts
; if the remote method returns a value, you must specify returns
.
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:
|
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 Default if not provided is the empty array, |
returns: {arg: 'greeting', type: 'string'} |
Important:
You can split long strings in the description
and notes
options into arrays of strings (lines) to keep line lengths manageable. For example:
[
"Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
"sed do eiusmod tempor incididunt ut labore et dolore",
"magna aliqua."
]
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. [ |
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.
|
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:
- String
- Buffer
- ReadableStream (anything that exposes
.pipe()
method)
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 Note that |
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):
- If there is an HTTP request parameter
args
with JSON content, then it uses the value ofargs['name']
. - 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:
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:
...
"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:
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:
module.exports = function(app) {
var remotes = app.remotes();
// modify all returned values
remotes.after('**', function (ctx, next) {
ctx.result = {
data: ctx.result
};
next();
});
};