App object

The App object represents a Loopback application.

The App object extends Express and supports Express / Connect middleware. See Express documentation for details.

var loopback = require('loopback');
var app = loopback();

app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(3000);

Properties

models

The app.models object has properties for all defined models. In the following example the Product and CustomerReceipt models are accessed using the models object.

NOTE: you must call app.boot() to create the app.models object.

var loopback = require('loopback');
var app = loopback();
app.boot({
  dataSources: {
    db: {connector: 'memory'}
  }
});
app.model('product', {dataSource: 'db'});
app.model('customer-receipt', {dataSource: 'db'});

// available based on the given name
var Product = app.models.Product;

// also available as camelCase
var product = app.models.product;

// multi-word models are avaiable as pascal cased
var CustomerReceipt = app.models.CustomerReceipt;

// also available as camelCase
var customerReceipt = app.models.customerReceipt;

Methods

app.boot([options])

Initialize an application from an options object or a set of JSON and JavaScript files.

What happens during an app boot?

  1. DataSources are created from an options.dataSources object or datasources.json in the current directory
  2. Models are created from an options.models object or models.json in the current directory
  3. Any JavaScript files in the ./models directory are loaded with require().

Options

  • cwd - optional - the directory to use when loading JSON and JavaScript files
  • models - optional - an object containing Model definitions
  • dataSources - optional - an object containing DataSource definitions

NOTE: mixing app.boot() and app.model(name, config) in multiple files may result in models being undefined due to race conditions. To avoid this when using app.boot() make sure all models are passed as part of the models definition.

Model Definitions

The following is an example of an object containing two Model definitions: "location" and "inventory".

{
  "dealership": {
    // a reference, by name, to a dataSource definition
    "dataSource": "my-db",
    // the options passed to Model.extend(name, properties, options)
    "options": {
      "relationships": {
        "cars": {
          "type": "hasMany",
          "model": "Car",
          "foreignKey": "dealerId"  
        }
      },
      "remoteMethods": {
        "nearby": {
          "description": "Find nearby locations around the geo point",
          "accepts": [
            {"arg": "here", "type": "GeoPoint", "required": true, "description": "geo location (lat & lng)"}
          ],
          "returns": {"arg": "locations", "root": true}
        }
      }
    },
    // the properties passed to Model.extend(name, properties, options)
    "properties": {
      "id": {"id": true},
      "name": "String",
      "zip": "Number",
      "address": "String"
    }
  },
  "car": {
    "dataSource": "my-db"
    "properties": {
      "id": {
        "type": "String",
        "required": true,
        "id": true
      },
      "make": {
        "type": "String",
        "required": true
      },
      "model": {
        "type": "String",
        "required": true
      }
    }
  }
}

Model definition properties

  • dataSource - required - a string containing the name of the data source definition to attach the Model to
  • options - optional - an object containing Model options
  • properties optional - an object defining the Model properties in LoopBack Definition Language

DataSource definition properties

  • connector - required - the name of the connector

model(name, definition)

Define a Model and export it for use by remote clients.

definition

  • public - default: true attach the Model to the app and export its methods to clients
  • dataSource - required the name of the DataSource to attach the Model to

Example:

// declare a DataSource
app.boot({
  dataSources: {
    db: {
      connector: 'mongodb',
      url: 'mongodb://localhost:27015/my-database-name'
    }
  }
});

// describe a model
var modelDefinition = {dataSource: 'db'};

// create the model
var Product = app.model('product', modelDefinition);

// use the model api
Product.create({name: 'pencil', price: 0.99}, console.log);

Note - This will expose all shared methods on the model.

You may also export an existing Model by calling app.model(Model) like the example below.

models()

Get the app's exported models. Only models defined using app.model() will show up in this list.

var models = app.models();

models.forEach(function (Model) {
  console.log(Model.modelName); // color
});

docs(options)

Enable swagger REST API documentation.

Options

Example

// enable docs
app.docs({basePath: 'http://localhost:3000'});

Run your app then navigate to the API explorer. Enter your API basepath to view your generated docs.

app.use( router )

Expose models over specified router. For example, to expose models over REST using the loopback.rest router:

app.use(loopback.rest());

View generated REST documentation at http://localhost:3000/_docs.

Middleware

LoopBack includes middleware similar to Express / Connect middleware.

loopback.token(options)

Options

  • cookies - An Array of cookie names
  • headers - An Array of header names
  • params - An Array of param names

Each array is used to add additional keys to find an accessToken for a request.

The following example illustrates how to check for an accessToken in a custom cookie, query string parameter and header called foo-auth.

app.use(loopback.token({
  cookies: ['foo-auth'],
  headers: ['foo-auth', 'X-Foo-Auth'],
  cookies: ['foo-auth', 'foo_auth']
}));

Defaults

By default the following names will be checked. These names are appended to any optional names. They will always be checked, but any names specified will be checked first.

  params.push('access_token');
  headers.push('X-Access-Token');
  headers.push('authorization');
  cookies.push('access_token');
  cookies.push('authorization');

NOTE: The loopback.token() middleware will only check for signed cookies.

Data Source object

A Loopback DataSource provides Models with the ability to manipulate data. Attaching a DataSource to a Model adds instance methods and static methods to the Model. The added methods may be remote methods.

Define a data source for persisting models.

var oracle = loopback.createDataSource({
  connector: 'oracle',
  host: '111.22.333.44',
  database: 'MYDB',
  username: 'username',
  password: 'password'
});

Methods

dataSource.createModel(name, properties, options)

Define a model and attach it to a DataSource.

var Color = oracle.createModel('color', {name: String});

dataSource.discoverModelDefinitions([username], fn)

Discover a set of model definitions (table or collection names) based on tables or collections in a data source.

oracle.discoverModelDefinitions(function (err, models) {
  models.forEach(function (def) {
    // def.name ~ the model name
    oracle.discoverSchema(null, def.name, function (err, schema) {
      console.log(schema);
    });
  });
});

dataSource.discoverSchema([owner], name, fn)

Discover the schema of a specific table or collection.

Example schema from oracle connector:

    {
      "name": "Product",
      "options": {
        "idInjection": false,
        "oracle": {
          "schema": "BLACKPOOL",
          "table": "PRODUCT"
        }
      },
      "properties": {
        "id": {
          "type": "String",
          "required": true,
          "length": 20,
          "id": 1,
          "oracle": {
            "columnName": "ID",
            "dataType": "VARCHAR2",
            "dataLength": 20,
            "nullable": "N"
          }
        },
        "name": {
          "type": "String",
          "required": false,
          "length": 64,
          "oracle": {
            "columnName": "NAME",
            "dataType": "VARCHAR2",
            "dataLength": 64,
            "nullable": "Y"
          }
        },
        "audibleRange": {
          "type": "Number",
          "required": false,
          "length": 22,
          "oracle": {
            "columnName": "AUDIBLE_RANGE",
            "dataType": "NUMBER",
            "dataLength": 22,
            "nullable": "Y"
          }
        },
        "effectiveRange": {
          "type": "Number",
          "required": false,
          "length": 22,
          "oracle": {
            "columnName": "EFFECTIVE_RANGE",
            "dataType": "NUMBER",
            "dataLength": 22,
            "nullable": "Y"
          }
        },
        "rounds": {
          "type": "Number",
          "required": false,
          "length": 22,
          "oracle": {
            "columnName": "ROUNDS",
            "dataType": "NUMBER",
            "dataLength": 22,
            "nullable": "Y"
          }
        },
        "extras": {
          "type": "String",
          "required": false,
          "length": 64,
          "oracle": {
            "columnName": "EXTRAS",
            "dataType": "VARCHAR2",
            "dataLength": 64,
            "nullable": "Y"
          }
        },
        "fireModes": {
          "type": "String",
          "required": false,
          "length": 64,
          "oracle": {
            "columnName": "FIRE_MODES",
            "dataType": "VARCHAR2",
            "dataLength": 64,
            "nullable": "Y"
          }
        }
      }
    }

dataSource.enableRemote(operation)

Enable remote access to a data source operation. Each connector has its own set of set remotely enabled and disabled operations. You can always list these by calling dataSource.operations().

dataSource.disableRemote(operation)

Disable remote access to a data source operation. Each connector has its own set of set enabled and disabled operations. You can always list these by calling dataSource.operations().

// all rest data source operations are
// disabled by default
var oracle = loopback.createDataSource({
  connector: require('loopback-connector-oracle'),
  host: '...',
  ...
});

// or only disable it as a remote method
oracle.disableRemote('destroyAll');

Notes:

  • disabled operations will not be added to attached models
  • disabling the remoting for a method only affects client access (it will still be available from server models)
  • data sources must enable / disable operations before attaching or creating models

dataSource.operations()

List the enabled and disabled operations.

console.log(oracle.operations());

Output:

{
  find: {
    remoteEnabled: true,
    accepts: [...],
    returns: [...]
    enabled: true
  },
  save: {
    remoteEnabled: true,
    prototype: true,
    accepts: [...],
    returns: [...],
    enabled: true
  },
  ...
}

GeoPoint object

The GeoPoint object represents a physical location.

Use the GeoPoint class.

var GeoPoint = require('loopback').GeoPoint;

Embed a latitude / longitude point in a Model.

var CoffeeShop = loopback.createModel('coffee-shop', {
  location: 'GeoPoint'
});

Loopback models with a GeoPoint property and an attached Data Source may be queried using geo-spatial filters and sorting.

For example, the following code is an an example of finding the three nearest coffee shops.

CoffeeShop.attachTo(oracle);
var here = new GeoPoint({lat: 10.32424, lng: 5.84978});
CoffeeShop.find({where: {location: {near: here}}, limit:3}, function(err, nearbyShops) {
  console.info(nearbyShops); // [CoffeeShop, ...]
});

Distance Types

Note: all distance methods use miles by default.

  • miles
  • radians
  • kilometers
  • meters
  • miles
  • feet
  • degrees

Methods

geoPoint.distanceTo(geoPoint, options)

Get the distance to another GeoPoint.

var here = new GeoPoint({lat: 10, lng: 10});
var there = new GeoPoint({lat: 5, lng: 5});
console.log(here.distanceTo(there, {type: 'miles'})); // 438

GeoPoint.distanceBetween(a, b, options)

Get the distance between two points.

GeoPoint.distanceBetween(here, there, {type: 'miles'}) // 438

Properties

geoPoint.lat

The latitude point in degrees. Range: -90 to 90.

geoPoint.lng

The longitude point in degrees. Range: -180 to 180.

Model object

A Loopback Model is a vanilla JavaScript class constructor with an attached set of properties and options. A Model instance is created by passing a data object containing properties to the Model constructor. A Model constructor will clean the object passed to it and only set the values matching the properties you define.

// valid color
var Color = loopback.createModel('color', {name: String});
var red = new Color({name: 'red'});
console.log(red.name); // red

// invalid color
var foo = new Color({bar: 'bat baz'});
console.log(foo.bar); // undefined

Properties

A model defines a list of property names, types and other validation metadata. A DataSource uses this definition to validate a Model during operations such as save().

Options

Some DataSources may support additional Model options.

Define A Loopbackmodel.

var User = loopback.createModel('user', {
  first: String,
  last: String,
  age: Number
});

Methods

Model.attachTo(dataSource)

Attach a model to a DataSource. Attaching a DataSource updates the model with additional methods and behaviors.

var oracle = loopback.createDataSource({
  connector: require('loopback-connector-oracle'),
  host: '111.22.333.44',
  database: 'MYDB',
  username: 'username',
  password: 'password'
});

User.attachTo(oracle);

Note: until a model is attached to a data source it will not have any attached methods.

Model.validatesFormatOf(property, options)

Require a model to include a property that matches the given format.

User.validatesFormat('name', {with: /\w+/});

Model.validatesPresenceOf(properties...)

Require a model to include a property to be considered valid.

User.validatesPresenceOf('first', 'last', 'age');

Model.validatesLengthOf(property, options)

Require a property length to be within a specified range.

User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}});

Model.validatesInclusionOf(property, options)

Require a value for property to be in the specified array.

User.validatesInclusionOf('gender', {in: ['male', 'female']});

Model.validatesExclusionOf(property, options)

Require a value for property to not exist in the specified array.

User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']});

Model.validatesNumericalityOf(property, options)

Require a value for property to be a specific type of Number.

User.validatesNumericalityOf('age', {int: true});

Model.validatesUniquenessOf(property, options)

Ensure the value for property is unique in the collection of models.

User.validatesUniquenessOf('email', {message: 'email is not unique'});

Note: not available for all connectors.

Currently supported in these connectors:

myModel.isValid()

Validate the model instance.

user.isValid(function (valid) {
    if (!valid) {
        console.log(user.errors);
        // => hash of errors
        // => {
        // =>   username: [errmessage, errmessage, ...],
        // =>   email: ...
        // => }    
    }
});

Properties

Model.properties

An object containing a normalized set of properties supplied to loopback.createModel(name, properties).

Example:

var props = {
  a: String,
  b: {type: 'Number'},
  c: {type: 'String', min: 10, max: 100},
  d: Date,
  e: loopback.GeoPoint
};

var MyModel = loopback.createModel('foo', props);

console.log(MyModel.properties);

Outputs:

{
  "a": {type: String},
  "b": {type: Number},
  "c": {
    "type": String,
    "min": 10,
    "max": 100
  },
  "d": {type: Date},
  "e": {type: GeoPoint},
  "id": {
    "id": 1
  }
}

CRUD and Query Mixins

Mixins are added by attaching a vanilla model to a data source with a connector. Each connector enables its own set of operations that are mixed into a Model as methods. To see available methods for a data source call dataSource.operations().

Log the available methods for a memory data source.

var ops = loopback
    .createDataSource({connector: loopback.Memory})
    .operations();

console.log(Object.keys(ops));

Outputs:

[ 'create',
  'updateOrCreate',
  'upsert',
  'findOrCreate',
  'exists',
  'findById',
  'find',
  'all',
  'findOne',
  'destroyAll',
  'deleteAll',
  'count',
  'include',
  'relationNameFor',
  'hasMany',
  'belongsTo',
  'hasAndBelongsToMany',
  'save',
  'isNewRecord',
  'destroy',
  'delete',
  'updateAttribute',
  'updateAttributes',
  'reload' ]

Here is the definition of the count() operation.

{
  accepts: [ { arg: 'where', type: 'object' } ],
  http: { verb: 'get', path: '/count' },
  remoteEnabled: true,
  name: 'count'
}

Static Methods

Note: These are the default mixin methods for a Model attached to a data source. See the specific connector for additional API documentation.

Model.create(data, [callback])

Create an instance of Model with given data and save to the attached data source. Callback is optional.

User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
  console.log(user instanceof User); // true
});

Note: You must include a callback and use the created model provided in the callback if your code depends on your model being saved or having an id.

Model.count([query], callback)

Query count of Model instances in data source. Optional query param allows to count filtered set of Model instances.

User.count({approved: true}, function(err, count) {
  console.log(count); // 2081
});

Model.find(filter, callback)

Find all instances of Model, matched by query. Fields used for filter and sort should be declared with {index: true} in model definition.

filter

  • where Object { key: val, key2: {gt: 'val2'}} The search criteria

    • Format: {key: val} or {key: {op: val}}
    • Operations:
      • gt: >
      • gte: >=
      • lt: <
      • lte: <=
      • between
      • inq: IN
      • nin: NOT IN
      • neq: !=
      • like: LIKE
      • nlike: NOT LIKE
  • include String, Object or Array Allows you to load relations of several objects and optimize numbers of requests.

    • Format:
      • 'posts': Load posts
      • ['posts', 'passports']: Load posts and passports
      • {'owner': 'posts'}: Load owner and owner's posts
      • {'owner': ['posts', 'passports']}: Load owner, owner's posts, and owner's passports
      • {'owner': [{posts: 'images'}, 'passports']}: Load owner, owner's posts, owner's posts' images, and owner's passports
  • order String The sorting order

    • Format: 'key1 ASC, key2 DESC'
  • limit Number The maximum number of instances to be returned

  • skip Number Skip the number of instances
  • offset Number Alias for skip

  • fields Object|Array|String The included/excluded fields

    • ['foo'] or 'foo' - include only the foo property
    • ['foo', 'bar'] - include the foo and bar properties
    • {foo: true} - include only foo
    • {bat: false} - include all properties, exclude bat

Find the second page of 10 users over age 21 in descending order exluding the password property.

User.find({
  where: {
    age: {gt: 21}},
    order: 'age DESC',
    limit: 10,
    skip: 10,
    fields: {password: false}
  },
  console.log
);

Note: See the specific connector's docs for more info.

Model.destroyAll(callback)

Delete all Model instances from data source. Note: destroyAll method does not perform destroy hooks.

Model.findById(id, callback)

Find instance by id.

User.findById(23, function(err, user) {
  console.info(user.id); // 23
});

Model.findOne(where, callback)

Find a single instance that matches the given where expression.

User.findOne({id: 23}, function(err, user) {
  console.info(user.id); // 23
});

Model.upsert(data, callback)

Update when record with id=data.id found, insert otherwise. Note: no setters, validations or hooks applied when using upsert.

Custom static methods

Define a static model method.

User.login = function (username, password, fn) {
  var passwordHash = hashPassword(password);
  this.findOne({username: username}, function (err, user) {
    var failErr = new Error('login failed');

    if(err) {
      fn(err);
    } else if(!user) {
      fn(failErr);
    } else if(user.password === passwordHash) {
      MyAccessTokenModel.create({userId: user.id}, function (err, accessToken) {
        fn(null, accessToken.id);
      });
    } else {
      fn(failErr);
    }
  });
}

Setup the static model method to be exposed to clients as a remote method.

loopback.remoteMethod(
  User.login,
  {
    accepts: [
      {arg: 'username', type: 'string', required: true},
      {arg: 'password', type: 'string', required: true}
    ],
    returns: {arg: 'sessionId', type: 'any'},
    http: {path: '/sign-in'}
  }
);

Instance methods

Note: These are the default mixin methods for a Model attached to a data source. See the specific connector for additional API documentation.

model.save([options], [callback])

Save an instance of a Model to the attached data source.

var joe = new User({first: 'Joe', last: 'Bob'});
joe.save(function(err, user) {
  if(user.errors) {
    console.log(user.errors);
  } else {
    console.log(user.id);
  }
});

model.updateAttributes(data, [callback])

Save specified attributes to the attached data source.

user.updateAttributes({
  first: 'updatedFirst',
  name: 'updatedLast'
}, fn);

model.destroy([callback])

Remove a model from the attached data source.

model.destroy(function(err) {
  // model instance destroyed
});

Custom instance methods

Define an instance method.

User.prototype.logout = function (fn) {
  MySessionModel.destroyAll({userId: this.id}, fn);
}

Define a remote model instance method.

loopback.remoteMethod(User.prototype.logout)

Relationships

Model.hasMany(Model, options)

Define a "one to many" relationship.

// by referencing model
Book.hasMany(Chapter);
// specify the name
Book.hasMany('chapters', {model: Chapter});

Query and create the related models.

Book.create(function(err, book) {
  // create a chapter instance
  // ready to be saved in the data source
  var chapter = book.chapters.build({name: 'Chapter 1'});

  // save the new chapter
  chapter.save();

  // you can also call the Chapter.create method with
  // the `chapters` property which will build a chapter
  // instance and save the it in the data source
  book.chapters.create({name: 'Chapter 2'}, function(err, savedChapter) {
    // this callback is optional
  });

  // query chapters for the book using the 
  book.chapters(function(err, chapters) {
    // all chapters with bookId = book.id
    console.log(chapters);
  });

  book.chapters({where: {name: 'test'}, function(err, chapters) {
    // all chapters with bookId = book.id and name = 'test'
    console.log(chapters);
  });
});

Model.belongsTo(Model, options)

A belongsTo relation sets up a one-to-one connection with another model, such that each instance of the declaring model "belongs to" one instance of the other model. For example, if your application includes users and posts, and each post can be written by exactly one user.

    Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});

The code above basically says Post has a reference called author to User using the userId property of Post as the foreign key. Now we can access the author in one of the following styles:

    post.author(callback); // Get the User object for the post author asynchronously
    post.author(); // Get the User object for the post author synchronously
    post.author(user) // Set the author to be the given user

Model.hasAndBelongsToMany(Model, options)

A hasAndBelongsToMany relation creates a direct many-to-many connection with another model, with no intervening model. For example, if your application includes users and groups, with each group having many users and each user appearing in many groups, you could declare the models this way,

    User.hasAndBelongsToMany('groups', {model: Group, foreignKey: 'groupId'});
    user.groups(callback); // get groups of the user
    user.groups.create(data, callback); // create a new group and connect it with the user
    user.groups.add(group, callback); // connect an existing group with the user
    user.groups.remove(group, callback); // remove the user from the group

Shared methods

Any static or instance method can be decorated as shared. These methods are exposed over the provided transport (eg. loopback.rest).

Remote methods and hooks

You can expose a Model's instance and static methods to clients. A remote method must accept a callback with the conventional fn(err, result, ...) signature.

loopback.remoteMethod(fn, [options])

Expose a remote method.

Product.stats = function(fn) {
  var statsResult = {
    totalPurchased: 123456
  };
  var err = null;

  // callback with an error and the result
  fn(err, statsResult);
}

loopback.remoteMethod(
  Product.stats,
  {
    returns: {arg: 'stats', type: 'object'},
    http: {path: '/info', verb: 'get'}
  }
);

Options

The options argument is a JSON object, described in the following table.

Option Required? Description
accepts No Describes the remote method's arguments; See Argument description. The callback argument is assumed; do not specify.
returns No Describes the remote method's callback arguments; See Argument description. The err argument is assumed; do not specify.
http No HTTP routing information:
  • http.path: path (relative to the model) at which the method is exposed. May be a path fragment (for example, /:myArg) that will be populated by an arg of the same name in the accepts description. For example, the stats method above will be at the whole path /products/stats.
  • http.verb: HTTP method (verb) from which the method is available (one of: get, post, put, del, or all).

Argument description

The arguments description defines either a single argument as an object or an ordered set of arguments as an array. Each individual argument has keys for:

  • arg: argument name
  • type: argument datatype; must be a loopback type.
  • required: Boolean value indicating if argument is required.
  • root: 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).
  • http: For input arguments: a function or an object describing mapping from HTTP request to the argument value, as explained below.

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'}
]

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

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 The value is looked up using req.param, which searches route arguments, the request body and the query string.
query An alias for form (see above).
path An alias for form (see above).
req The whole HTTP reqest object is used as the value.

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

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

The use the second way to specify HTTP mapping for input parameters, specify a custom mapping function that looks like this:

{
  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 as the value
    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 a HTTP request parameter args with a JSON content, then the value of args['name'] is used if it is defined.
  2. Otherwise req.param('name') is returned.

Remote hooks

Run a function before or after a remote method is called by a client.

// *.save === prototype.save
User.beforeRemote('*.save', function(ctx, user, next) {
  if(ctx.user) {
    next();
  } else {
    next(new Error('must be logged in to update'))
  }
});

User.afterRemote('*.save', function(ctx, user, next) {
  console.log('user has been saved', user);
  next();
});

Remote hooks also support wildcards. Run a function before any remote method is called.

// ** will match both prototype.* and *.*
User.beforeRemote('**', function(ctx, user, next) {
  console.log(ctx.methodString, 'was invoked remotely'); // users.prototype.save was invoked remotely
  next();
});

Other wildcard examples

// run before any static method eg. User.find
User.beforeRemote('*', ...);

// run before any instance method eg. User.prototype.save
User.beforeRemote('prototype.*', ...);

// prevent password hashes from being sent to clients
User.afterRemote('**', function (ctx, user, next) {
  if(ctx.result) {
    if(Array.isArray(ctx.result)) {
      ctx.result.forEach(function (result) {
        result.password = undefined;
      });
    } else {
      ctx.result.password = undefined;
    }
  }

  next();
});

Context

Remote hooks are provided with a Context ctx object which contains transport specific data (eg. for http: req and res). The ctx object also has a set of consistent apis across transports.

ctx.user

A Model representing the user calling the method remotely. Note: this is undefined if the remote method is not invoked by a logged in user.

ctx.result

During afterRemote hooks, ctx.result will contain the data about to be sent to a client. Modify this object to transform data before it is sent.

Rest

When loopback.rest is used the following ctx properties are available.

ctx.req

The express ServerRequest object. See full documentation.

ctx.res

The express ServerResponse object. See full documentation.

create

Create a new instance of the model and persist it into the data source

Definition

POST /locations

Arguments

  • data The model instance data

Example

Request:

curl -X POST -H "Content-Type:application/json" \
-d '{"name": "L1", "street": "107 S B St", "city": "San Mateo", "zipcode": "94401"}' \
http://localhost:3000/locations

Response:

{
  "id": "96",
  "street": "107 S B St",
  "city": "San Mateo",
  "zipcode": 94401,
  "name": "L1",
  "geo": {
    "lat": 37.5670042,
    "lng": -122.3240212
  }
}

Errors

None

upsert

Update an existing model instance or insert a new one into the data source

Definition

PUT /locations

Arguments

  • data The model instance data

Examples

Request - insert:

curl -X PUT -H "Content-Type:application/json" \
-d '{"name": "L1", "street": "107 S B St", "city": "San Mateo", "zipcode": "94401"}' \
http://localhost:3000/locations

Response:

{
  "id": "98",
  "street": "107 S B St",
  "city": "San Mateo",
  "zipcode": 94401,
  "name": "L1",
  "geo": {
    "lat": 37.5670042,
    "lng": -122.3240212
  }
}

Request - update:

curl -X PUT -H "Content-Type:applicatin/json" \
-d '{"id": "98", "name": "L4", "street": "107 S B St", "city": "San Mateo", \
"zipcode": "94401"}' http://localhost:3000/locations

Response:

{
  "id": "98",
  "street": "107 S B St",
  "city": "San Mateo",
  "zipcode": 94401,
  "name": "L4"
}

Errors

None

exists

Check whether a model instance exists by ID in the data source.

Definition

GET /locations/exists

Arguments

  • id The model id

Example

Request:

curl http://localhost:3000/locations/88/exists

Response:

{
    "exists": true
}

Errors

None

findById

Find a model instance by ID from the data source.

Definition

GET /locations/{id}

Arguments

  • id The model id

Example

Request:

curl http://localhost:3000/locations/88

Response:

{
    "id": "88",
    "street": "390 Lang Road",
    "city": "Burlingame",
    "zipcode": 94010,
    "name": "Bay Area Firearms",
    "geo": {
        "lat": 37.5874391,
        "lng": -122.3381437
    }
}

Errors

None

find

Find all instances of the model matched by filter from the data source.

Definition

GET /locations

Arguments

Pass the arguments as the value of the find HTTP query parameter, as follows

/modelName?filter=[filterType1]=<val1>&filter[filterType2]=<val2>...

where filterType1, filterType2, and so on, are the filter types, and val1, val2 are the corresponding values, as described in the following table.

Filter type Type Description
where Object Search criteria. Format: {key: val} or {key: {op: val}} For list of valid operations, see Operations, below.
include String, Object, or Array Allows you to load relations of several objects and optimize numbers of requests. For format, see Include format, below.
order String Sort order. Format: 'key1 ASC, key2 DESC', where ASC specifies ascending and DESC specifies descending order.
limit Number Maximum number of instances to return.
skip (offset) Number Skip the specified number of instances. Use offset as alternative.
fields Object, Array, or String The included/excluded fields. For foramt, see fields below.

Operations available in where filter:

  • gt: >
  • gte: >=
  • lt: <
  • lte: <=
  • between
  • inq: IN
  • nin: NOT IN
  • neq: !=
  • like: LIKE
  • nlike: NOT LIKE

Include format:

  • 'posts': Load posts
  • ['posts', 'passports']: Load posts and passports
  • {'owner': 'posts'}: Load owner and owner's posts
  • {'owner': ['posts', 'passports']}: Load owner, owner's posts, and owner's passports
  • {'owner': [{posts: 'images'}, 'passports']}: Load owner, owner's posts, owner's posts' images, and owner's passports

Fields format:

  • ['foo'] or 'foo' - include only the foo property
  • ['foo', 'bar'] - include the foo and bar properties
  • {foo: true} - include only foo
  • {bat: false} - include all properties, exclude bat

For example,

  • '/weapons': Weapons
  • '/weapons?filter[limit]=2&filter[offset]=5': Paginated Weapons
  • '/weapons?filter[where][name]=M1911': Weapons with name M1911
  • '/weapons?filter[where][audibleRange][lt]=10': Weapons with audioRange < 10
  • '/weapons?filter[fields][name]=1&filter[fields][effectiveRange]=1': Only name and effective ranges
  • '/weapons?filter[where][effectiveRange][gt]=900&filter[limit]=3': The top 3 weapons with a range over 900 meters
  • '/weapons?filter[order]=audibleRange%20DESC&filter[limit]=3': The loudest 3 weapons

  • '/locations': Locations

  • '/locations?filter[where][geo][near]=153.536,-28.1&filter[limit]=3': The 3 closest locations to a given geo point

Example

Request:

Find without filter:

curl http://localhost:3000/locations

Find with a filter:

curl http://localhost:3000/locations?filter%5Blimit%5D=2

Note: For curl, [ needs to be encoded as %5B, and ] as %5D.

Response:

[
  {
    "id": "87",
    "street": "7153 East Thomas Road",
    "city": "Scottsdale",
    "zipcode": 85251,
    "name": "Phoenix Equipment Rentals",
    "geo": {
      "lat": 33.48034450000001,
      "lng": -111.9271738
    }
  },
  {
    "id": "88",
    "street": "390 Lang Road",
    "city": "Burlingame",
    "zipcode": 94010,
    "name": "Bay Area Firearms",
    "geo": {
      "lat": 37.5874391,
      "lng": -122.3381437
    }
  }
]

Errors

None

findOne

Find first instance of the model matched by filter from the data source.

Definition

GET /locations/findOne

Arguments

  • filter The filter that defines where, order, fields, skip, and limit. It's same as find's filter argument. Please see find for more details.

Example

Request:

curl http://localhost:3000/locations/findOne?filter%5Bwhere%5D%5Bcity%5D=Scottsdale

Response:

{
  "id": "87",
  "street": "7153 East Thomas Road",
  "city": "Scottsdale",
  "zipcode": 85251,
  "name": "Phoenix Equipment Rentals",
  "geo": {
    "lat": 33.48034450000001,
    "lng": -111.9271738
  }
}

Errors

None

deleteById

Delete a model instance by id from the data source

Definition

DELETE /locations/{id}

Arguments

  • id The model id

Example

Request:

curl -X DELETE http://localhost:3000/locations/88

Response:

Example TBD.

Errors

None

count

Count instances of the model matched by where from the data source

Definition

GET /locations/count

Arguments

  • where The criteria to match model instances

Example

Request - count without "where" filter

curl http://localhost:3000/locations/count

Request - count with a "where" filter

curl http://localhost:3000/locations/count?where%5bcity%5d=Burlingame

Response:

{
    count: 6
}

Errors

None

nearby

Find nearby locations around the geo point.

Definition

GET /locations/nearby

Arguments

  • here geo location object with lat and lng properties
  • page number of pages (page size=10)
  • max max distance in miles

Example

Request:

curl http://localhost:3000/locations/nearby?here%5Blat%5D=37.587409&here%5Blng%5D=-122.338225

Response:

[
  {
    "id": "88",
    "street": "390 Lang Road",
    "city": "Burlingame",
    "zipcode": 94010,
    "name": "Bay Area Firearms",
    "geo": {
      "lat": 37.5874391,
      "lng": -122.3381437
    }
  },
  {
    "id": "89",
    "street": "1850 El Camino Real",
    "city": "Menlo Park",
    "zipcode": 94027,
    "name": "Military Weaponry",
    "geo": {
      "lat": 37.459525,
      "lng": -122.194253
    }
  }
]

Errors

None

updateAttributes

Update attributes for a model instance and persist it into the data source

Definition

PUT /locations/{id}

Arguments

  • data An object containing property name/value pairs
  • id The model id

Example

Request:

curl -X PUT -H "Content-Type:application/json" -d '{"name": "L2"}' \
http://localhost:3000/locations/88

Response:

{
  "id": "88",
  "street": "390 Lang Road",
  "city": "Burlingame",
  "zipcode": 94010,
  "name": "L2",
  "geo": {
    "lat": 37.5874391,
    "lng": -122.3381437
  },
  "state": "CA"
}

Errors

  • 404 No instance found for the given id

getAssociatedModel

Follow the relations from one model (location) to another one (inventory) to get instances of the associated model.

Definition

GET /locations/{id}/inventory

Arguments

  • id The id for the location model

Example

Request:

curl http://localhost:3000/locations/88/inventory

Response:

[
  {
    "productId": "2",
    "locationId": "88",
    "available": 10,
    "total": 10
  },
  {
    "productId": "3",
    "locationId": "88",
    "available": 1,
    "total": 1
  }
]

Errors

None