## Node.js API ### App Create a Loopback application. ```js var loopback = require('loopback'); var app = loopback(); app.get('/', function(req, res){ res.send('hello world'); }); app.listen(3000); ``` > **Notes** > > - extends [express](http://expressjs.com/api.html#express) > - see [express docs](http://expressjs.com/api.html) for details > - supports [express / connect middleware](http://expressjs.com/api.html#middleware) #### 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". ```js { "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](http://docs.strongloop.com/loopback-datasource-juggler/#loopback-definition-language) **DataSource Definition Properties** - `connector` - **required** - the name of the [connector](#working-with-data-sources-and-connectors) #### app.model(name, definition) Define a `Model` and export it for use by remote clients. ```js // 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](#shared-methods) on the model. You may also export an existing `Model` by calling `app.model(Model)` like the example below. #### app.models.MyModel All models are avaialbe from the `loopback.models` object. In the following example the `Product` and `CustomerReceipt` models are accessed using the `models` object. > **NOTE:** you must call `app.boot()` in order to build the app.models object. ```js 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; ``` #### app.models() Get the app's exported models. Only models defined using `app.model()` will show up in this list. ```js var models = app.models(); models.forEach(function (Model) { console.log(Model.modelName); // color }); ``` #### app.docs(options) Enable swagger REST API documentation. **Options** - `basePath` The basepath for your API - eg. 'http://localhost:3000'. **Example** ```js // enable docs app.docs({basePath: 'http://localhost:3000'}); ``` Run your app then navigate to [the api explorer](http://petstore.swagger.wordnik.com/). 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: ```js app.use(loopback.rest()); ``` View generated REST documentation by visiting: [http://localhost:3000/_docs](http://localhost:3000/_docs). ### Middleware LoopBack comes bundled with several `connect` / `express` style 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`. ```js 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. ```js 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](http://expressjs.com/api.html#req.signedCookies). ### Model 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. ```js // 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](#data-source) uses this definition to validate a `Model` during operations such as `save()`. **Options** Some [DataSources](#data-source) may support additional `Model` options. Define A Loopbackmodel. ```js var User = loopback.createModel('user', { first: String, last: String, age: Number }); ``` #### Validation (expiremental) ##### Model.validatesFormatOf(property, options) Require a model to include a property that matches the given format. ```js User.validatesFormat('name', {with: /\w+/}); ``` ##### Model.validatesPresenceOf(properties...) Require a model to include a property to be considered valid. ```js User.validatesPresenceOf('first', 'last', 'age'); ``` ##### Model.validatesLengthOf(property, options) Require a property length to be within a specified range. ```js 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. ```js User.validatesInclusionOf('gender', {in: ['male', 'female']}); ``` ##### Model.validatesExclusionOf(property, options) Require a value for `property` to not exist in the specified array. ```js User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']}); ``` ##### Model.validatesNumericalityOf(property, options) Require a value for `property` to be a specific type of `Number`. ```js User.validatesNumericalityOf('age', {int: true}); ``` ##### Model.validatesUniquenessOf(property, options) Ensure the value for `property` is unique in the collection of models. ```js User.validatesUniquenessOf('email', {message: 'email is not unique'}); ``` **Note:** not available for all [connectors](#connectors). Currently supported in these connectors: - [In Memory](#memory-connector) - [Oracle](http://github.com/strongloop/loopback-connector-oracle) - [MongoDB](http://github.com/strongloop/loopback-connector-mongodb) ##### myModel.isValid() Validate the model instance. ```js user.isValid(function (valid) { if (!valid) { console.log(user.errors); // => hash of errors // => { // => username: [errmessage, errmessage, ...], // => email: ... // => } } }); ``` #### Model.properties An object containing a normalized set of properties supplied to `loopback.createModel(name, properties)`. Example: ```js 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: ```js { "a": {type: String}, "b": {type: Number}, "c": { "type": String, "min": 10, "max": 100 }, "d": {type: Date}, "e": {type: GeoPoint}, "id": { "id": 1 } } ``` #### Model.attachTo(dataSource) Attach a model to a [DataSource](#data-source). Attaching a [DataSource](#data-source) updates the model with additional methods and behaviors. ```js 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**. #### CRUD and Query Mixins Mixins are added by attaching a vanilla model to a [data source](#data-source) with a [connector](#connectors). Each [connector](#connectors) 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. ```js var ops = loopback .createDataSource({connector: loopback.Memory}) .operations(); console.log(Object.keys(ops)); ``` Outputs: ```js [ '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. ```js { 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. ```js 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. ```js 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. ```js 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](#connectors) 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. ```js 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. ```js 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. ```js 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](#remote-method). ```js loopback.remoteMethod( User.login, { accepts: [ {arg: 'username', type: 'string', required: true}, {arg: 'password', type: 'string', required: true} ], returns: {arg: 'accessTokenId', 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. ```js 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. ```js user.updateAttributes({ first: 'updatedFirst', name: 'updatedLast' }, fn); ``` ##### model.destroy([callback]) Remove a model from the attached data source. ```js model.destroy(function(err) { // model instance destroyed }); ``` ##### Custom Instance Methods Define an instance method. ```js User.prototype.logout = function (fn) { MyAccessTokenModel.destroyAll({userId: this.id}, fn); } ``` Define a remote model instance method. ```js loopback.remoteMethod(User.prototype.logout) ``` #### Remote Methods Both instance and static methods can be exposed to clients. A remote method must accept a callback with the conventional `fn(err, result, ...)` signature. ##### loopback.remoteMethod(fn, [options]) Expose a remote method. ```js 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** - **accepts** - (optional) an arguments description specifying the remote method's arguments. - **returns** - (optional) an arguments description specifying the remote methods callback arguments. - **http** - (advanced / optional, object) http routing info - **http.path** - the path relative to the model the method will be exposed at. May be a path fragment (eg. '/:myArg') which 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** - (get, post, put, del, all) - the route verb the method will be available from. **Argument Description** An arguments description defines either a single argument as an object or an ordered set of arguments as an array. ```js // examples {arg: 'myArg', type: 'number'} [ {arg: 'arg1', type: 'number', required: true}, {arg: 'arg2', type: 'array'} ] ``` **Types** Each argument may define any of the [loopback types](#loopback-types). **Notes:** - The callback is an assumed argument and does not need to be specified in the accepts array. - The err argument is also assumed and does not need to be specified in the returns array. #### Remote Hooks Run a function before or after a remote method is called by a client. ```js // *.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. ```js // ** 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 ```js // 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](#loopbackrest) is used the following `ctx` properties are available. ###### ctx.req The express ServerRequest object. [See full documentation](http://expressjs.com/api.html#req). ###### ctx.res The express ServerResponse object. [See full documentation](http://expressjs.com/api.html#res). #### Relationships ##### Model.hasMany(Model, options) Define a "one to many" relationship. ```js // by referencing model Book.hasMany(Chapter); // specify the name Book.hasMany('chapters', {model: Chapter}); ``` Query and create the related models. ```js 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. ```js 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: ```js 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, ```js 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](#rest)). ### Data Source A Loopback `DataSource` provides [Models](#model) with the ability to manipulate data. Attaching a `DataSource` to a `Model` adds [instance methods](#instance-methods) and [static methods](#static-methods) to the `Model`. The added methods may be [remote methods](#remote-methods). Define a data source for persisting models. ```js var oracle = loopback.createDataSource({ connector: 'oracle', host: '111.22.333.44', database: 'MYDB', username: 'username', password: 'password' }); ``` #### dataSource.createModel(name, properties, options) Define a model and attach it to a `DataSource`. ```js 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. ```js 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:** ```js { "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](#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](#connector) has its own set of set enabled and disabled operations. You can always list these by calling `dataSource.operations()`. ```js // 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: ```js { find: { remoteEnabled: true, accepts: [...], returns: [...] enabled: true }, save: { remoteEnabled: true, prototype: true, accepts: [...], returns: [...], enabled: true }, ... } ``` #### Connectors Create a data source with a specific connector. See **available connectors** for specific connector documentation. ```js var memory = loopback.createDataSource({ connector: loopback.Memory }); ``` **Database Connectors** - [In Memory](#memory-connector) - [Oracle](http://github.com/strongloop/loopback-connector-oracle) - [MongoDB](http://github.com/strongloop/loopback-connector-mongodb) - [MySQL](http://github.com/strongloop/loopback-connector-mysql) - In Development - [SQLite3](http://github.com/strongloop/loopback-connector-sqlite) - In Development - [Postgres](http://github.com/strongloop/loopback-connector-postgres) - In Development - [Redis](http://github.com/strongloop/loopback-connector-redis) - In Development - [CouchDB](http://github.com/strongloop/loopback-connector-couch) - In Development - [Firebird](http://github.com/strongloop/loopback-connector-firebird) - In Development **Other Connectors** - [REST](http://github.com/strongloop/loopback-connector-rest) - [Email](#email-model) - [JSON RPC](http://github.com/strongloop/loopback-connector-jsonrpc) - In Development - [SOAP](http://github.com/strongloop/loopback-connector-soap) - In Development - [Push Notifications](https://github.com/strongloop/loopback-push-notification) - In Development - [File Storage](https://github.com/strongloop/loopback-storage-service) - In Development **Installing Connectors** Include the connector in your package.json dependencies and run `npm install`. ```js { "dependencies": { "loopback-connector-oracle": "latest" } } ``` ##### Memory Connector The built-in memory connector allows you to test your application without connecting to an actual persistent data source, such as a database. Although the memory connector is very well tested it is not recommended to be used in production. Creating a data source using the memory connector is very simple. ```js // use the built in memory function // to create a memory data source var memory = loopback.memory(); // or create it using the standard // data source creation api var memory = loopback.createDataSource({ connector: loopback.Memory }); // create a model using the // memory data source var properties = { name: String, price: Number }; var Product = memory.createModel('product', properties); Product.create([ {name: 'apple', price: 0.79}, {name: 'pear', price: 1.29}, {name: 'orange', price: 0.59}, ], count); function count() { Product.count(console.log); // 3 } ``` **CRUD / Query** The memory connector supports all the standard [query and crud operations](#crud-and-query-mixins) to allow you to test your models against an in memory data source. **GeoPoint Filtering** The memory connector also supports geo-filtering when using the `find()` operation with an attached model. See [GeoPoint](#geopoint) for more information on geo-filtering. ### GeoPoint Use the `GeoPoint` class. ```js var GeoPoint = require('loopback').GeoPoint; ``` Embed a latitude / longitude point in a [Model](#model). ```js var CoffeeShop = loopback.createModel('coffee-shop', { location: 'GeoPoint' }); ``` Loopback Model's with a GeoPoint property and an attached DataSource may be queried using geo spatial filters and sorting. Find the 3 nearest coffee shops. ```js 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, ...] }); ``` #### geoPoint.distanceTo(geoPoint, options) Get the distance to another `GeoPoint`. ```js 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. ```js GeoPoint.distanceBetween(here, there, {type: 'miles'}) // 438 ``` #### Distance Types **Note:** all distance methods use `miles` by default. - `miles` - `radians` - `kilometers` - `meters` - `miles` - `feet` - `degrees` #### geoPoint.lat The latitude point in degrees. Range: -90 to 90. #### geoPoint.lng The longitude point in degrees. Range: -180 to 180.