diff --git a/README.md b/README.md index 72273445..1bb02afd 100644 --- a/README.md +++ b/README.md @@ -1,1114 +1,9 @@ -# loopback +# LoopBack -v0.9.0 +## Documentation -## Install +[See the full documentation](http://docs.strongloop.com/loopback) - slnode install loopback -g - -## Server API +## Issues - - [App](#app) - - [Model](#model) - - [DataSource](#data-source) - - [Connectors](#connectors) - - [Loopback Types](#loopback-types) - - [GeoPoint](#geo-point) - - [REST Router](#rest-router) - - [Bundled Models](#bundled-models) - - [User](#user-model) - - [Session](#session-model) - - [Email](#email-model) - -## Client API - -_TODO_ - -### App - -Create a Loopback application. - - 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.model(Model) - -Expose a `Model` to remote clients. - - // create a testing data source - var memory = loopback.memory(); - var Color = memory.createModel('color', {name: String}); - Color.attachTo(memory); - - app.model(Color); - app.use(loopback.rest()); - -**Note:** this will expose all [shared methods](#shared-methods) on the model. - -#### app.models() - -Get the app's exposed models. - - 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** - - // 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. - -### 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. - - // 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. - - 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. - - 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](#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. - - user.isValid(function (valid) { - if (!valid) { - user.errors // hash of errors {attr: [errmessage, errmessage, ...], attr: ...} - } - }); - -#### 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 - } - } - -#### Model.attachTo(dataSource) - -Attach a model to a [DataSource](#data-source). Attaching a [DataSource](#data-source) 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**. - -#### 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. - - 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'}} - - **include** `String`, `Object` or `Array`. - - **order** `String` - - **limit** `Number` - - **skip** `Number` - - **fields** `Object|Array|String` - - `['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](#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. - - 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) { - MySessionModel.create({userId: user.id}, function (err, session) { - fn(null, session.id); - }); - } else { - fn(failErr); - } - }); - } - -Setup the static model method to be exposed to clients as a [remote method](#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); - -#### 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. - - Product.stats = function(fn) { - var calc = require('./stats'); - - Product.find(function(err, products) { - var productStats = calc(products); - fn(null, productStats); - }); - } - - 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. A - - **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. - - // 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. - - // *.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](#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). - -Access the raw `req` object for the remote method call. - -#### Relationships - -##### Model.hasMany(Model) - -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); - }); - }); - -#### 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. - - 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`. - - 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](#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()`. - - // 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 - }, - ... - } - -#### Connectors - -Create a data source with a specific connector. See **available connectors** for specific connector documentation. - - var memory = loopback.createDataSource({ - connector: loopback.Memory - }); - -**Available Connectors** - - - [In Memory](#memory-connector) - - [REST](http://github.com/strongloop/loopback-connector-rest) - - [Oracle](http://github.com/strongloop/loopback-connector-oracle) - - [MongoDB](http://github.com/strongloop/loopback-connector-mongodb) - - TODO - [MySQL](http://github.com/strongloop/loopback-connector-mysql) - - TODO - [SQLite3](http://github.com/strongloop/loopback-connector-sqlite) - - TODO - [Postgres](http://github.com/strongloop/loopback-connector-postgres) - - TODO - [Redis](http://github.com/strongloop/loopback-connector-redis) - - TODO - [CouchDB](http://github.com/strongloop/loopback-connector-couch) - - TODO - [Firebird](http://github.com/strongloop/loopback-connector-firebird) - -**Installing Connectors** - -Include the connector in your package.json dependencies and run `npm install`. - - { - "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. - - // 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 - } - -###### Operations - -**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. - - var GeoPoint = require('loopback').GeoPoint; - -Embed a latitude / longitude point in a [Model](#model). - - 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. - - 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`. - - 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 - -#### 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. - -### Loopback Types - -Various APIs in Loopback accept type descriptions (eg. [remote methods](#remote-methods), [loopback.createModel()](#model)). The following is a list of supported types. - - - `null` - JSON null - - `Boolean` - JSON boolean - - `Number` - JSON number - - `String` - JSON string - - `Object` - JSON object - - `Array` - JSON array - - `Date` - a JavaScript date object - - `Buffer` - a node.js Buffer object - - [GeoPoint](#geopoint) - A Loopback GeoPoint object. - -## Bundled Models - -The Loopback library is unopinioned in the way you define your app's data and logic. Loopback also bundles useful pre-built models for common use cases. - - - User - register and authenticate users of your app locally or against 3rd party services. - - Email - send emails to your app users using smtp or 3rd party services. - -Defining a model with `loopback.createModel()` is really just extending the base `loopback.Model` type using `loopback.Model.extend()`. The bundled models extend from the base `loopback.Model` allowing you to extend them arbitrarily. - -### User Model - -Register and authenticate users of your app locally or against 3rd party services. - -#### Define a User Model - -Extend a vanilla Loopback model using the built in User model. - - // create a data source - var memory = loopback.memory(); - - // define a User model - var User = loopback.User.extend('user'); - - // attach to the memory connector - User.attachTo(memory); - - // also attach the session model to a data source - User.session.attachTo(memory); - - // expose over the app's api - app.model(User); - -**Note:** By default the `loopback.User` model uses the `loopback.Session` model to persist sessions. You can change this by setting the `session` property. - -**Note:** You must attach both the `User` and `User.session` model's to a data source! - -#### User Creation - -Create a user like any other model. - - // username and password are not required - User.create({email: 'foo@bar.com', password: 'bar'}, function(err, user) { - console.log(user); - }); - - -#### Login a User - -Create a session for a user using the local auth strategy. - -**Node.js** - - User.login({username: 'foo', password: 'bar'}, function(err, session) { - console.log(session); - }); - -**REST** - -You must provide a username and password over rest. To ensure these values are encrypted, include these as part of the body and make sure you are serving your app over https (through a proxy or using the https node server). - - POST - - /users/login - ... - { - "email": "foo@bar.com", - "password": "bar" - } - - ... - - 200 OK - { - "sid": "1234abcdefg", - "uid": "123" - } - -#### Logout a User - -**Node.js** - - // login a user and logout - User.login({"email": "foo@bar.com", "password": "bar"}, function(err, session) { - User.logout(session.id, function(err) { - // user logged out - }); - }); - - // logout a user (server side only) - User.findOne({email: 'foo@bar.com'}, function(err, user) { - user.logout(); - }); - -**REST** - - POST /users/logout - ... - { - "sid": "" - } - -#### Verify Email Addresses - -Require a user to verify their email address before being able to login. This will send an email to the user containing a link to verify their address. Once the user follows the link they will be redirected to `/` and be able to login normally. - - User.requireEmailVerfication = true; - User.afterRemote('create', function(ctx, user, next) { - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.com', - subject: 'Thanks for Registering at FooBar', - text: 'Please verify your email address!' - template: 'verify.ejs', - redirect: '/' - }; - - user.verify(options, next); - }); - - -#### Send Reset Password Email - -Send an email to the user's supplied email address containing a link to reset their password. - - User.reset(email, function(err) { - console.log('email sent'); - }); - -#### Remote Password Reset - -The password reset email will send users to a page rendered by loopback with fields required to reset the user's password. You may customize this template by defining a `resetTemplate` setting. - - User.settings.resetTemplate = 'reset.ejs'; - -#### Remote Password Reset Confirmation - -Confirm the password reset. - - User.confirmReset(token, function(err) { - console.log(err || 'your password was reset'); - }); - - -### Session Model - -Identify users by creating sessions when they connect to your loopback app. By default the `loopback.User` model uses the `loopback.Session` model to persist sessions. You can change this by setting the `session` property. - - // define a custom session model - var MySession = loopback.Session.extend('my-session'); - - // define a custom User model - var User = loopback.User.extend('user'); - - // use the custom session model - User.session = MySession; - - // attach both Session and User to a data source - User.attachTo(loopback.memory()); - MySession.attachTo(loopback.memory()); - -### Email Model - -Send emails from your loopback app. - -### REST Router - -Expose models over rest using the `loopback.rest` router. - - app.use(loopback.rest()); - -**REST Documentation** - -View generated REST documentation by visiting: [http://localhost:3000/_docs](http://localhost:3000/_docs). - -### SocketIO Middleware (Not Available) - -**Coming Soon** - Expose models over socket.io using the `loopback.sio()` middleware. - - app.use(loopback.sio); - +File any issues [here on GitHub](https://github.com/strongloop/loopback/issues) diff --git a/docs.json b/docs.json index 1bcbbf6a..cbd44add 100644 --- a/docs.json +++ b/docs.json @@ -6,8 +6,14 @@ "docs/gettingstarted.md", "docs/resources.md", "docs/concepts.md", - {"title": "API", "depth": 2}, + "docs/cli.md", "docs/api.md", + "docs/ios.md", + "docs/js.md", + "docs/java.md", "docs/rest.md" - ] + ], + "assets": { + "/": "/docs" + } } diff --git a/docs/api.md b/docs/api.md index cb8ec8e2..5cf8fd13 100644 --- a/docs/api.md +++ b/docs/api.md @@ -889,18 +889,26 @@ var memory = loopback.createDataSource({ }); ``` -**Available Connectors** +**Database Connectors** - [In Memory](#memory-connector) - - [REST](http://github.com/strongloop/loopback-connector-rest) - [Oracle](http://github.com/strongloop/loopback-connector-oracle) - [MongoDB](http://github.com/strongloop/loopback-connector-mongodb) - - TODO - [MySQL](http://github.com/strongloop/loopback-connector-mysql) - - TODO - [SQLite3](http://github.com/strongloop/loopback-connector-sqlite) - - TODO - [Postgres](http://github.com/strongloop/loopback-connector-postgres) - - TODO - [Redis](http://github.com/strongloop/loopback-connector-redis) - - TODO - [CouchDB](http://github.com/strongloop/loopback-connector-couch) - - TODO - [Firebird](http://github.com/strongloop/loopback-connector-firebird) + - [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** @@ -949,8 +957,6 @@ function count() { } ``` -###### Operations - **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. diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 00000000..8afafaa2 --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,40 @@ +## Command Line + +The StrongLoop Suite comes bundled with a command line tool called StrongLoop +Command or `slc`. StrongLoop Command allows you to create boilerplate for +LoopBack and other StrongNode applications. + +### Commands + +`slc lb` provides the following commands. + +#### workspace + +Initialize a workspace as a new empty directory with an optional +name. The default name is "loopback-workspace". + +```sh +$ slc lb workspace my-loopback-workspace +``` + +#### api + +Create a LoopBack application in a new directory within a workspace +using the given name. The name arg is required. + +```sh +$ cd my-loopback-workspace +$ slc lb api my-app +$ slc run app +``` + +#### model +Create a model in an existing LoopBack application. If you provide the +`-i` or `--interactive` flags, you will be prompted through a model +configuration. The `--data-source` flag allows you to specify the name of a +custom data. Otheriwse it will use the data source named "db". + +```sh +$ cd my-app +$ slc lb model product +``` diff --git a/docs/concepts.md b/docs/concepts.md index 3d10032a..d6f0c39a 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -7,7 +7,7 @@ answer a couple of questions: > What _is_ LoopBack? - - A component in the [StrongLoop Suite](www.strongloop.com/strongloop-suite). + - A component in the [StrongLoop Suite](http://www.strongloop.com/strongloop-suite). - A library of Node.js modules for connecting mobile apps to a variety of data sources. - A command line tool, `slc lb`, for generating models and entire applications @@ -16,39 +16,49 @@ answer a couple of questions: > How does LoopBack work? -LoopBack Applications are made up of three components: -[Data Sources](#data-sources-and-connectors) (also referred to as "Connectors"), -[Models](#models), and the [Mobile Clients](#mobile-clients) that consume them. + - LoopBack applications are made up of three components: +[Models](#models), [Data Sources/Connectors](#data-sources-and-connectors), and the +[Mobile Clients](#mobile-clients) that consume them. -Any mobile or web app can interact with a LoopBack Data Source through the Model -API. The Model API is available [locally within Node.js](#model), [remotely over -REST](#rest-api), and as native mobile SDKs for [iOS, Android, and -HTML5](#mobile-clients). Using the API, clients can query databases, store data, -upload files, send emails, create push notifications, register users, and any -other behavior provided by data sources. + - Any mobile or web app can interact with LoopBack through the Model API that are +backed by various data sources. The Model API is available +[locally within Node.js](#model), [remotely over REST](#rest-api), and as native +mobile SDKs for [iOS, Android, and HTML5](#mobile-clients). Using the API, +clients can query databases, store data, upload files, send emails, create push +notifications, register users, and any other behavior provided by data sources. + +![concepts](loopback-concepts.png "LoopBack Concepts") ### Mobile Clients **PLACEHOLDER FOR SDK INTRO** +In summary, mobile clients work with objects that are connected to LoopBack +to provide data and perform actions.Those objects are captured as models in +LoopBack. + ### Models > What is a Model? -In LoopBack, a Model consists of the following: +A LoopBack Model consists of the following: - Application data - Validation rules + - Data access capabilities - Business logic A mobile client uses the remote API provided by Models to request any -information needed to display a useful interface to the user. +information needed to display a useful interface to the user or trigger actions +on the models to interact with backend systems. -#### A Simple Example +Let's use a simple example to explain what a model can do for you. + +#### Defining a model For example, an e-commerce app might have `Product` and `Inventory` Models. A mobile client could use the `Product` Model API to search through all of the -Procuts in a database. A client could join the `Product` and `Inventory` data to +Products in a database. A client could join the `Product` and `Inventory` data to determine what products are in stock, or the `Product` Model could provide a server-side function (or [remote method](#remote-methods)) that aggregates this information. @@ -60,17 +70,22 @@ var Product = Model.extend('product'); var Inventory = Model.extend('customer'); ``` -**NOTE:** Models are _schema-less_ by default, but some Connectors, such as +**NOTE:** Models are _schema-less_ by default, but some data sources, such as relational databases, _require_ schemas. Additionally, schemas are immensely -valuable for validating sanitizing data coming from mobile clients. See -[Sanitizing and Validating Models](#sanitizing-and-validating-models) if your -application needs to connect to an RDBMS, for example. +valuable for establishing the common knowledge of business data so that the data +exchange can be agreed and documented while data coming from mobile clients can +be validated and/or sanitized . See [Sanitizing and Validating Models](#sanitizing-and-validating-models) +if your application needs to connect to an RDBMS, for example. -#### Attaching Data Sources +#### Attaching to Data Sources -Attaching a Model to a Data Source gives you access to a powerful API mixed into -Models by their Sources. The [MongoDB Connector](#), for example, mixes in a -`create` method that allows us to store a new Product in the database: +Instances of a Model carry application data. But they are not very interesting +until applications can create, retrieve, update, or delete (CRUD) model instances. +LoopBack introduces the DataSource concept to provide the data access +capabilities to models. Attaching a Model to a DataSource gives you access to a +powerful API mixed into Models by the Connector behind a DataSource. The +[MongoDB Connector](#), for example, mixes in a `create` method that allows us +to store a new Product in the database: ```js // Step 2: Attach Data Sources @@ -87,6 +102,9 @@ Product.create({ name: 'widget', price: 99.99 }, function(err, widget) { }); ``` +Now the models have both data and behaviors. How can the mobile clients benefit +from them? We need a way to make the models available from mobile clients. + #### Exposing to Mobile Clients Models can be exposed to mobile clients using one of the remoting middlewares. @@ -104,11 +122,16 @@ app.use(loopback.rest()); app.model(Product); ``` +After this, you'll have the `Product` model with CRUD functions working remotely +from the mobile clients. Please note the model is schema-less till now and the +data are not checked. + #### Sanitizing and Validating Models -Once a schema is added to a Model, it will validate and sanitize data before -giving it to a Data Source. For example, the `Product` Model has a schema that -will not change. The example below updates the `Product` Model with a schema +A Model can be described in plain json or JavaScript. The description is called +schema. Once a schema is defined for a Model, it will validate and sanitize data +before giving it to a Data Source. For example, the `Product` Model has a schema +that will not change. The example below updates the `Product` Model with a schema written in **LoopBack Definition Language**, a well-documented flavor of JSON. ```js @@ -137,12 +160,8 @@ required `name` property. ### Data Sources and Connectors -LoopBack allows you to connect to many sources of data and services both in the -cloud and on-premise in your data center. These sources of data and services are -called DataSources. DataSources are accessed through a plugin called a Connector -in LoopBack. Plugins are highly customizable and extensible. Unlike other -mobile backend, LoopBack can leverage your existing data and organize them in -the form of models. +Now you see the power of LoopBack models. A model gets rich set of functions out +of the box with the contribution from Data Sources and Connectors. The concept of DataSource is introduced to encapsulate business logic to exchange data between models and various data sources. Data sources are @@ -150,10 +169,16 @@ typically databases that provide create, retrieve, update, and delete (CRUD) functions. LoopBack also generalize other backend services, such as REST APIs, SOAP Web Services, and Storage Services, as data sources. -Data sources are backed by connectors which implement the data exchange logic -using database drivers or other client APIs. In general, connectors are not used -directly by application code. The DataSource class provides APIs to configure -the underlying connector and exposes functions via DataSource or model classes. +LoopBack allows you to connect to many sources of data and services both in the +cloud and on-premise in your data center. DataSources are accessed through a +plugin called a Connector in LoopBack. Plugins are highly customizable and +extensible. Unlike other mobile backend, LoopBack can leverage your existing +data and organize them in the form of models. + +Connectors implement the data exchange logic using database drivers or other +client APIs. In general, connectors are not used directly by application code. +The DataSource class provides APIs to configure the underlying connector and +exposes functions via DataSource or model classes. #### LoopBack Connector Modules @@ -168,7 +193,7 @@ For more information, please read the [LoopBack DataSource and Connector Guide]( ### REST -Everything defined in LoopBack (e.g. Models) can be made available as a REST +Functions defined in LoopBack Models can be made available as a REST endpoint. You can see and experiment with _your_ REST api using the [LoopBack API Explorer](http://localhost:3000/explorer/). diff --git a/docs/ios.md b/docs/ios.md new file mode 100644 index 00000000..2f3fe317 --- /dev/null +++ b/docs/ios.md @@ -0,0 +1,3 @@ +## iOS API + +

Stay tuned. Currently in development.

diff --git a/docs/java.md b/docs/java.md new file mode 100644 index 00000000..caa1b594 --- /dev/null +++ b/docs/java.md @@ -0,0 +1,3 @@ +## Android API + +

Stay tuned. Currently in development.

diff --git a/docs/js.md b/docs/js.md new file mode 100644 index 00000000..5c6f4422 --- /dev/null +++ b/docs/js.md @@ -0,0 +1,3 @@ +## Browser API + +

Stay tuned. Currently in development.

diff --git a/docs/loopback-concepts.png b/docs/loopback-concepts.png new file mode 100644 index 00000000..4232b35c Binary files /dev/null and b/docs/loopback-concepts.png differ