loopback/README.md

981 lines
28 KiB
Markdown
Raw Normal View History

2013-04-09 16:02:36 +00:00
# asteroid
2013-06-25 20:52:14 +00:00
v0.9.0
2013-04-09 16:02:36 +00:00
## Install
2013-04-09 18:33:29 +00:00
slnode install asteroid -g
2013-04-26 18:44:19 +00:00
2013-06-06 20:50:46 +00:00
## Server API
2013-04-26 18:44:19 +00:00
2013-06-05 14:38:04 +00:00
- [App](#app)
2013-06-06 20:50:46 +00:00
- [Model](#model)
- [DataSource](#data-source)
- [Connectors](#connectors)
2013-06-06 20:50:46 +00:00
- [GeoPoint](#geo-point)
2013-06-05 19:13:55 +00:00
- [Asteroid Types](#asteroid-types)
2013-06-06 20:50:46 +00:00
- [REST Router](#rest-router)
2013-04-26 18:44:19 +00:00
2013-06-06 20:50:46 +00:00
## Client API
2013-04-26 18:44:19 +00:00
2013-06-05 14:38:04 +00:00
_TODO_
### App
2013-06-06 00:11:21 +00:00
Create an asteroid application.
2013-06-05 14:38:04 +00:00
var asteroid = require('asteroid');
var app = asteroid();
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.
2013-06-11 16:01:44 +00:00
var memory = asteroid.createDataSource({connector: asteroid.Memory});
var Color = memory.createModel('color', {name: String});
2013-06-05 14:38:04 +00:00
app.model(Color);
app.use(asteroid.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) {
2013-06-11 16:01:44 +00:00
console.log(Model.modelName); // color
2013-06-05 14:38:04 +00:00
});
### Model
2013-06-11 16:01:44 +00:00
An Asteroid `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.
2013-06-06 20:30:52 +00:00
2013-06-11 16:01:44 +00:00
// valid color
var Color = asteroid.createModel('color', {name: String});
2013-06-06 20:30:52 +00:00
var red = new Color({name: 'red'});
2013-06-11 16:01:44 +00:00
console.log(red.name); // red
// invalid color
var foo = new Color({bar: 'bat baz'});
console.log(foo.bar); // undefined
2013-06-05 14:38:04 +00:00
**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()`.
2013-06-11 16:01:44 +00:00
**Options**
2013-06-05 14:38:04 +00:00
2013-06-11 16:01:44 +00:00
Some [DataSources](#data-source) may support additional `Model` options.
2013-06-05 14:38:04 +00:00
Define an asteroid model.
var User = asteroid.createModel('user', {
first: String,
last: String,
age: Number
});
2013-06-06 20:30:52 +00:00
### Validation (expiremental)
2013-06-06 20:30:52 +00:00
#### 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.
User.validatesUniquenessOf('email', {message: 'email is not unique'});
**Note:** not available for all [connectors](#connectors).
#### myModel.isValid()
Validate the model instance.
user.isValid(function (valid) {
if (!valid) {
user.errors // hash of errors {attr: [errmessage, errmessage, ...], attr: ...}
}
});
2013-06-28 01:26:44 +00:00
#### Model.properties
2013-06-28 01:28:21 +00:00
An object containing a normalized set of properties supplied to `asteroid.createModel(name, properties)`.
2013-06-28 01:26:44 +00:00
Example:
var props = {
a: String,
b: {type: 'Number'},
c: {type: 'String', min: 10, max: 100},
d: Date,
e: asteroid.GeoPoint
};
var MyModel = asteroid.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
}
}
2013-06-05 14:38:04 +00:00
#### 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 = asteroid.createDataSource({
2013-06-20 21:17:38 +00:00
connector: require('asteroid-connector-oracle'),
2013-06-05 14:38:04 +00:00
host: '111.22.333.44',
database: 'MYDB',
username: 'username',
password: 'password'
});
User.attachTo(oracle);
2013-06-06 20:50:46 +00:00
**Note:** until a model is attached to a data source it will **not** have any **attached methods**.
2013-06-05 14:38:04 +00:00
#### CRUD and Query Mixins
Mixins are added by attaching a vanilla model to a data source with a connector. Each [connector](#connectors) enables its own set of operations that are attached to a `Model` as methods. To see available methods for a data source with a connector call `dataSource.operations()`.
2013-06-06 00:44:46 +00:00
#### Static Methods
2013-06-06 00:44:46 +00:00
2013-06-21 21:59:30 +00:00
##### Model.create(data, [callback])
2013-06-06 00:44:46 +00:00
2013-06-21 21:59:30 +00:00
Create an instance of Model with given data and save to the attached data source. Callback is optional.
2013-06-06 00:44:46 +00:00
User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
console.log(user instanceof User); // true
});
2013-06-21 21:59:30 +00:00
**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`.
2013-06-06 00:44:46 +00:00
##### Model.count([query], callback)
2013-06-06 00:44:46 +00:00
Query count of Model instances in data source. Optional query param allows to count filtered set of Model instances.
2013-06-06 00:44:46 +00:00
User.count({approved: true}, function(err, count) {
console.log(count); // 2081
2013-06-06 00:44:46 +00:00
});
##### Model.find(filter, callback)
2013-06-06 00:44:46 +00:00
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`
2013-06-06 00:44:46 +00:00
2013-06-06 20:50:46 +00:00
Find the second page of 10 users over age 21 in descending order.
User.find({where: {age: {gt: 21}}, order: 'age DESC', limit: 10, skip: 10})
2013-06-06 20:50:46 +00:00
**Note:** See the specific connector's [docs](#connectors) for more info.
2013-06-06 00:44:46 +00:00
##### Model.destroyAll(callback)
2013-06-06 00:44:46 +00:00
Delete all Model instances from data source. **Note:** destroyAll method does not perform destroy hooks.
2013-06-06 00:44:46 +00:00
##### Model.findById(id, callback)
Find instance by id.
User.findById(23, function(err, user) {
console.info(user.id); // 23
2013-06-06 00:44:46 +00:00
});
2013-06-21 00:31:23 +00:00
##### Model.findOne(where, callback)
2013-06-21 00:31:23 +00:00
Find a single instance that matches the given where expression.
2013-06-21 00:31:23 +00:00
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
2013-06-05 14:38:04 +00:00
Define a static model method.
2013-06-20 21:17:38 +00:00
2013-06-05 14:38:04 +00:00
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);
}
});
}
2013-06-20 21:17:38 +00:00
Setup the static model method to be exposed to clients as a [remote method](#remote-method).
2013-06-05 19:13:55 +00:00
2013-06-06 00:11:21 +00:00
asteroid.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'}
}
);
2013-06-05 14:38:04 +00:00
#### Instance Methods
##### 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
2013-06-06 00:11:21 +00:00
Define an instance method.
2013-06-05 14:38:04 +00:00
User.prototype.logout = function (fn) {
MySessionModel.destroyAll({userId: this.id}, fn);
}
2013-06-06 00:11:21 +00:00
Define a remote model instance method.
2013-06-05 14:38:04 +00:00
2013-06-11 16:01:44 +00:00
asteroid.remoteMethod(User.prototype.logout);
2013-06-05 19:13:55 +00:00
2013-06-05 19:17:29 +00:00
#### Remote Methods
2013-06-05 19:13:55 +00:00
2013-06-20 21:17:38 +00:00
Both instance and static methods can be exposed to clients. A remote method must accept a callback with the conventional `fn(err, result, ...)` signature.
2013-06-05 19:13:55 +00:00
2013-06-11 16:01:44 +00:00
##### asteroid.remoteMethod(fn, [options]);
2013-06-05 19:13:55 +00:00
2013-06-05 19:17:29 +00:00
Expose a remote method.
2013-06-05 19:13:55 +00:00
Product.stats = function(fn) {
myApi.getStats('products', fn);
}
2013-06-06 00:16:00 +00:00
asteroid.remoteMethod(
Product.stats,
{
returns: {arg: 'stats', type: 'array'},
http: {path: '/info', verb: 'get'}
}
);
2013-06-05 19:13:55 +00:00
**Options**
2013-06-05 19:17:29 +00:00
- **accepts** - (optional) an arguments description specifying the remote method's arguments. A
- **returns** - (optional) an arguments description specifying the remote methods callback arguments.
2013-06-05 19:13:55 +00:00
- **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`.
2013-06-05 19:13:55 +00:00
- **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.
2013-06-06 00:11:21 +00:00
// examples
{arg: 'myArg', type: 'number'}
[
{arg: 'arg1', type: 'number', required: true},
{arg: 'arg2', type: 'array'}
]
2013-06-05 19:13:55 +00:00
**Types**
Each argument may define any of the [asteroid types](#asteroid-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.
2013-06-05 14:38:04 +00:00
#### Remote Hooks
2013-06-05 19:13:55 +00:00
Run a function before or after a remote method is called by a client.
2013-06-05 14:38:04 +00:00
2013-06-12 22:44:38 +00:00
// *.save === prototype.save
User.beforeRemote('*.save', function(ctx, user, next) {
if(ctx.user) {
2013-06-05 14:38:04 +00:00
next();
} else {
next(new Error('must be logged in to update'))
}
});
2013-06-12 22:44:38 +00:00
User.afterRemote('*.save', function(ctx, user, next) {
2013-06-11 16:01:44 +00:00
console.log('user has been saved', user);
next();
});
2013-06-12 22:44:38 +00:00
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
2013-06-12 22:44:38 +00:00
User.beforeRemote('*', ...);
// run before any instance method eg. User.prototype.save
User.beforeRemote('prototype.*', ...);
2013-06-13 04:30:20 +00:00
// 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();
});
2013-06-05 19:13:55 +00:00
#### Context
2013-06-12 22:44:38 +00:00
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.
2013-06-05 19:13:55 +00:00
2013-06-12 22:44:38 +00:00
##### ctx.user
2013-06-05 19:13:55 +00:00
2013-06-12 22:44:38 +00:00
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.
2013-06-05 19:13:55 +00:00
2013-06-13 04:30:20 +00:00
##### 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.
2013-06-05 19:13:55 +00:00
##### Rest
When [asteroid.rest](#asteroidrest) 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) {
2013-06-20 21:17:38 +00:00
// create a chapter instance
// ready to be saved in the data source
var chapter = book.chapters.build({name: 'Chapter 1'});
2013-06-05 19:13:55 +00:00
2013-06-20 21:17:38 +00:00
// save the new chapter
chapter.save();
2013-06-05 19:13:55 +00:00
2013-06-20 21:17:38 +00:00
// 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
});
2013-06-05 19:13:55 +00:00
2013-06-20 21:17:38 +00:00
// query chapters for the book using the
2013-06-05 19:13:55 +00:00
book.chapters(function(err, chapters) {
2013-06-20 21:17:38 +00:00
// all chapters with bookId = book.id
console.log(chapters);
2013-06-05 19:13:55 +00:00
});
book.chapters({where: {name: 'test'}, function(err, chapters) {
// all chapters with bookId = book.id and name = 'test'
2013-06-20 21:17:38 +00:00
console.log(chapters);
2013-06-05 19:13:55 +00:00
});
});
##### Model.hasAndBelongsToMany()
TODO: implement / document
2013-06-05 14:38:04 +00:00
#### Shared Methods
Any static or instance method can be decorated as `shared`. These methods are exposed over the provided transport (eg. [asteroid.rest](#rest)).
### Data Source
2013-06-05 19:17:29 +00:00
An Asteroid `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).
2013-06-05 14:38:04 +00:00
Define a data source for persisting models.
var oracle = asteroid.createDataSource({
connector: 'oracle',
2013-06-05 14:38:04 +00:00
host: '111.22.333.44',
database: 'MYDB',
username: 'username',
password: 'password'
2013-04-26 18:44:19 +00:00
});
2013-06-11 16:01:44 +00:00
#### dataSource.createModel(name, properties, options)
2013-04-26 18:44:19 +00:00
2013-06-05 14:38:04 +00:00
Define a model and attach it to a `DataSource`.
2013-04-26 18:44:19 +00:00
2013-06-05 14:38:04 +00:00
var Color = oracle.createModel('color', {name: String});
2013-04-26 18:44:19 +00:00
2013-06-20 21:17:38 +00:00
#### dataSource.discoverModelDefinitions([username], fn)
2013-06-05 19:13:55 +00:00
2013-06-20 21:17:38 +00:00
Discover a set of model definitions (table or collection names) based on tables or collections in a data source.
2013-06-20 17:17:55 +00:00
2013-06-20 21:17:38 +00:00
oracle.discoverModelDefinitions(function (err, models) {
2013-06-20 17:17:55 +00:00
models.forEach(function (def) {
// def.name ~ the model name
oracle.discoverSchema(null, def.name, function (err, schema) {
console.log(schema);
});
});
2013-06-05 19:13:55 +00:00
});
2013-06-05 14:38:04 +00:00
2013-06-20 17:17:55 +00:00
#### dataSource.discoverSchema([owner], name, fn)
2013-06-05 19:13:55 +00:00
2013-06-20 21:17:38 +00:00
Discover the schema of a specific table or collection.
2013-06-11 16:01:44 +00:00
2013-06-20 21:17:38 +00:00
**Example schema from oracle connector:**
2013-06-11 16:01:44 +00:00
2013-06-20 21:17:38 +00:00
{
"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"
}
}
}
2013-06-11 16:01:44 +00:00
}
2013-06-12 22:44:38 +00:00
#### dataSource.enableRemote(operation)
2013-06-12 22:44:38 +00:00
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()`.
2013-06-12 22:44:38 +00:00
#### dataSource.disableRemote(operation)
2013-06-12 22:44:38 +00:00
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 = asteroid.createDataSource({
2013-06-20 21:17:38 +00:00
connector: require('asteroid-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: {
2013-06-11 16:01:44 +00:00
remoteEnabled: true,
accepts: [...],
returns: [...]
enabled: true
},
2013-06-11 16:01:44 +00:00
save: {
remoteEnabled: true,
prototype: true,
accepts: [...],
returns: [...],
enabled: true
},
...
}
2013-06-06 20:30:52 +00:00
#### Connectors
Create a data source with a specific connector. See **available connectors** for specific connector documentation.
2013-06-06 00:11:21 +00:00
var memory = asteroid.createDataSource({
2013-06-11 16:01:44 +00:00
connector: asteroid.Memory
2013-06-06 00:11:21 +00:00
});
**Available Connectors**
2013-06-06 00:11:21 +00:00
- [Oracle](http://github.com/strongloop/asteroid-connectors/oracle)
- [In Memory](http://github.com/strongloop/asteroid-connectors/memory)
- TODO - [REST](http://github.com/strongloop/asteroid-connectors/rest)
- TODO - [MySQL](http://github.com/strongloop/asteroid-connectors/mysql)
- TODO - [SQLite3](http://github.com/strongloop/asteroid-connectors/sqlite)
- TODO - [Postgres](http://github.com/strongloop/asteroid-connectors/postgres)
- TODO - [Redis](http://github.com/strongloop/asteroid-connectors/redis)
- TODO - [MongoDB](http://github.com/strongloop/asteroid-connectors/mongo)
- TODO - [CouchDB](http://github.com/strongloop/asteroid-connectors/couch)
- TODO - [Firebird](http://github.com/strongloop/asteroid-connectors/firebird)
**Installing Connectors**
Include the connector in your package.json dependencies and run `npm install`.
{
"dependencies": {
2013-06-20 21:17:38 +00:00
"asteroid-connector-oracle": "latest"
}
}
2013-06-06 00:11:21 +00:00
2013-06-24 23:30:09 +00:00
### GeoPoint
Use the `GeoPoint` class.
var GeoPoint = require('asteroid').GeoPoint;
2013-06-05 14:38:04 +00:00
2013-06-05 19:13:55 +00:00
Embed a latitude / longitude point in a [Model](#model).
var CoffeeShop = asteroid.createModel('coffee-shop', {
location: 'GeoPoint'
});
Asteroid 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.
2013-06-24 22:22:25 +00:00
CoffeeShop.attachTo(oracle);
2013-06-24 22:32:22 +00:00
var here = new GeoPoint({lat: 10.32424, lng: 5.84978});
CoffeeShop.find({where: {location: {near: here}}, limit:3}, function(err, nearbyShops) {
2013-06-05 19:13:55 +00:00
console.info(nearbyShops); // [CoffeeShop, ...]
});
2013-06-05 14:38:04 +00:00
2013-06-05 19:13:55 +00:00
#### geoPoint.distanceTo(geoPoint, options)
2013-06-05 14:38:04 +00:00
2013-06-05 19:13:55 +00:00
Get the distance to another `GeoPoint`.
2013-06-05 14:38:04 +00:00
2013-06-24 22:32:22 +00:00
var here = new GeoPoint({lat: 10, lng: 10});
var there = new GeoPoint({lat: 5, lng: 5});
2013-06-05 19:13:55 +00:00
console.log(here.distanceTo(there, {type: 'miles'})); // 438
#### GeoPoint.distanceBetween(a, b, options)
2013-06-05 14:38:04 +00:00
2013-06-05 19:13:55 +00:00
Get the distance between two points.
2013-06-05 14:38:04 +00:00
2013-06-05 19:13:55 +00:00
GeoPoint.distanceBetween(here, there, {type: 'miles'}) // 438
2013-06-05 14:38:04 +00:00
2013-06-05 19:13:55 +00:00
#### Distance Types
2013-06-05 14:38:04 +00:00
2013-06-24 23:30:09 +00:00
**Note:** all distance methods use `miles` by default.
2013-06-07 16:49:33 +00:00
- `miles`
- `radians`
- `kilometers`
2013-06-24 23:30:09 +00:00
- `meters`
- `miles`
- `feet`
- `degrees`
2013-06-05 14:38:04 +00:00
2013-06-05 19:13:55 +00:00
#### geoPoint.lat
2013-06-05 14:38:04 +00:00
2013-06-05 19:13:55 +00:00
The latitude point in degrees. Range: -90 to 90.
2013-04-09 16:02:36 +00:00
2013-06-24 22:32:22 +00:00
#### geoPoint.lng
2013-04-09 16:02:36 +00:00
2013-06-05 19:13:55 +00:00
The longitude point in degrees. Range: -180 to 180.
2013-05-20 21:31:04 +00:00
2013-06-05 19:13:55 +00:00
### Asteroid Types
2013-05-20 21:31:04 +00:00
2013-06-06 00:11:21 +00:00
Various APIs in Asteroid accept type descriptions (eg. [remote methods](#remote-methods), [asteroid.createModel()](#model)). The following is a list of supported types.
2013-05-20 21:31:04 +00:00
2013-06-05 19:13:55 +00:00
- `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
2013-06-20 21:17:38 +00:00
- [GeoPoint](#geopoint) - an asteroid GeoPoint object. TODO
2013-06-26 03:29:58 +00:00
2013-07-01 00:38:53 +00:00
#### Bundled Models
The Asteroid library is unopinioned in the way you define your app's data and logic. Asteroid also bundles useful pre-built models for common use cases.
- User - _TODO_ register and authenticate users of your app locally or against 3rd party services.
- Notification - _TODO_ create, store, schedule and send push notifications to your app users.
- Email - _TODO_ schedule and send emails to your app users using smtp or 3rd party services.
- Job - _TODO_ schedule arbitrary code to run at a given time.
2013-07-01 18:46:41 +00:00
Defining a model with `asteroid.createModel()` is really just extending the base `asteroid.Model` type using `asteroid.Model.extend()`. The bundled models extend from the base `asteroid.Model` allowing you to extend them arbitrarily.
2013-07-01 00:38:53 +00:00
2013-06-26 03:29:58 +00:00
### User Model
2013-07-01 00:38:53 +00:00
Register and authenticate users of your app locally or against 3rd party services.
#### Create a User Model
Extend a vanilla Asteroid model using the built in User model.
2013-06-26 03:29:58 +00:00
// define a User model
2013-07-01 23:50:03 +00:00
var User = asteroid.User.extend('user');
2013-07-01 00:38:53 +00:00
2013-06-26 03:29:58 +00:00
// attach to the memory connector
User.attachTo(memory);
2013-07-01 00:38:53 +00:00
// expose over the app's api
app.model(User);
#### 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);
});
#### Authentication Strategies (Using Passport.js)
Setup an authentication strategy.
[See all available providers from passport.js](http://passportjs.org/guide/providers/).
// first add your model to your app
app.model(User);
// by default your User model has a local strategy similar to below
// customize your own
// disable the default local strategy
User.useLocalStrategy(false);
// create a custom strategy
var LocalStrategy = require('passport-local').Strategy;
2013-07-01 23:50:03 +00:00
passport.use(new LocalStrategy(function(username, password, done) {
2013-07-01 00:38:53 +00:00
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
user.comparePassword(password, function(err, isMatch) {
if (err) return done(err);
if(isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
#### Login a User
Create a session for a user. When called remotely the password is required.
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"
}
**Note:** The `uid` type will be the same type you specify when creating your model. In this case it is a string.
#### Logout a User
User.logout({username: 'foo'}, function(err) {
console.log(err);
});
**Note:** When calling this method remotely, the first argument will automatically be populated with the current user's id. If the caller is not logged in the method will fail with an error status code `401`.
#### Verify Email Addresses
To require email verification before a user is allowed to login, supply a verification property with a `verify` settings object.
// define a User model
2013-07-01 18:46:41 +00:00
var User = asteroid.User.extend(
2013-07-01 00:38:53 +00:00
'user',
{
email: {
type: 'EmailAddress',
username: true
},
password: {
hideRemotely: true, // default for Password
type: 'Password',
min: 4,
max: 26
},
verified: {
hideRemotely: true,
type: 'Boolean',
verify: {
// the model field
// that contains the email
// to verify
email: 'email',
2013-07-01 23:50:03 +00:00
template: 'email.ejs',
redirect: '/'
2013-07-01 00:38:53 +00:00
}
}
},
{
// the model field
// that contains the user's email
// for verification and password reset
// defaults to 'email'
email: 'email',
resetTemplate: 'reset.ejs'
}
);
2013-07-01 18:46:41 +00:00
When a user is created (on the server or remotely) and the verification property exists, an email is sent to the field that corresponds to `verify.email` or `options.email`. The email contains a link the user must navigate to in order to verify their email address. Once they verify, users are allowed to login normally. Otherwise login attempts will respond with a 'must verify' error.
2013-07-01 00:38:53 +00:00
#### Send Reset Password Email
Send an email to the user's supplied email address containing a link to reset their password.
User.sendResetPasswordEmail(email, function(err) {
// email sent
2013-06-26 03:29:58 +00:00
});
2013-07-01 00:38:53 +00:00
#### Remote Password Reset
The password reset email will send users to a page rendered by asteroid with fields required to reset the user's password. You may customize this template by providing a `resetTemplate` option when defining your user model.
2013-06-26 03:29:58 +00:00
### Email Model
Send emails from your asteroid app.
2013-06-06 20:50:46 +00:00
### REST Router
2013-06-06 00:11:21 +00:00
2013-06-06 20:50:46 +00:00
Expose models over rest using the `asteroid.rest` router.
2013-06-06 00:11:21 +00:00
app.use(asteroid.rest());
2013-06-06 20:50:46 +00:00
**REST Documentation**
View generated REST documentation by visiting: [http://localhost:3000/_docs](http://localhost:3000/_docs).
2013-06-06 00:11:21 +00:00
### SocketIO Middleware **Not Available**
**Coming Soon** - Expose models over socket.io using the `asteroid.sio()` middleware.
app.use(asteroid.sio);