12 KiB
asteroid
v0.0.1
Install
slnode install asteroid -g
Server APIs
Client APIs
TODO
App
Create an asteroid application.
var asteroid = require('asteroid');
var app = asteroid();
app.get('/', function(req, res){
res.send('hello world');
});
app.listen(3000);
Notes:
- extends express
- see express docs for details
- supports express / connect middleware
app.model(Model)
Expose a Model
to remote clients.
var memory = asteroid.createDataSource({adapter: 'memory'});
var Color = memory.defineModel({name: String});
app.model(Color);
app.use(asteroid.rest());
Note: this will expose all shared methods on the model.
app.models()
Get the app's exposed models.
var models = app.models();
models.forEach(function (Model) {
console.log(Model.name); // color
});
Model
An Asteroid Model
is a vanilla JavaScript class constructor with an attached set of properties and settings.
Properties
A model defines a list of property names, types and other validation metadata. A DataSource uses this definition to validate a Model
during operations such as save()
.
Settings
Some DataSources may support additional Model
settings.
Define an asteroid model.
var User = asteroid.createModel('user', {
first: String,
last: String,
age: Number
});
Model.attachTo(dataSource)
Attach a model to a DataSource. Attaching a DataSource updates the model with additional methods and behaviors.
var oracle = asteroid.createDataSource({
adapter: '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 only expose the API defined below.
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);
}
});
}
Expose the static model method to clients as a remote method.
User.expose('login', {
accepts: [
{arg: 'username', type: 'string', required: true},
{arg: 'password', type: 'string', required: true}
],
returns: {arg: 'sessionId', type: 'any'}
});
Instance Methods
Define an instance method
User.prototype.logout = function (fn) {
MySessionModel.destroyAll({userId: this.id}, fn);
}
Expose the model instance method to clients using remoting.
User.prototype.logout.shared = true;
User.expose('logout', {instance: true});
Remote Methods
Both instance and static methods may be shared exposed to clients using remoting. A remote method must accept a callback with the conventional fn(err, result, ...)
signature.
Model.expose(method, [options]);
Expose a remote method.
Product.stats = function(fn) {
myApi.getStats('products', fn);
}
Product.expose('stats', {
returns: {arg: 'stats', type: 'array'},
http: {path: '/info', verb: 'get'}
});
Options
- instance - (default: false) specify the remote method is an instance method
- 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 relative path 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.
- 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 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.
Hooks
Run a function before or after a model method is called.
User.before('save', function(user, next) {
console.log('about to save', user);
next();
});
Prevent the method from being called by proding an error.
User.before('delete', function(user, next) {
// prevent all delete calls
next(new Error('deleting is disabled'));
});
Remote Hooks
Run a function before or after a remote method is called by a client.
User.beforeRemote('save', function(ctx, user, next) {
if(ctx.user.id === user.id) {
next();
} else {
next(new Error('must be logged in to update'))
}
});
Context
Remote hooks are provided with a Context ctx
that contains raw access to the transport specific objects. The ctx
object also has a set of consistent apis that are consistent across transports.
ctx.me
The id of the user calling the method remotely. Note: this is undefined if a user is not logged in.
Rest
When asteroid.rest is used the following ctx
properties are available.
ctx.req
The express ServerRequest object. See full documentation.
ctx.res
The express ServerResponse object. See full documentation.
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) {
// using 'chapters' scope for build:
var c = book.chapters.build({name: 'Chapter 1'});
// same as:
c = new Chapter({name: 'Chapter 1', bookId: book.id});
// using 'chapters' scope for create:
book.chapters.create();
// same as:
Chapter.create({bookId: book.id});
// using scope for querying:
book.chapters(function(err, chapters) {
/* all chapters with bookId = book.id */
});
book.chapters({where: {name: 'test'}, function(err, chapters) {
// all chapters with bookId = book.id and name = 'test'
});
});
Model.hasAndBelongsToMany()
TODO: implement / document
Model.availableHooks()
Return a list of available hooks.
console.log(User.availableHooks()); // ['save', ...]
Shared Methods
Any static or instance method can be decorated as shared
. These methods are exposed over the provided transport (eg. asteroid.rest).
Model.availableMethods()
Returns the currently available api of a model as well as descriptions of any modified behavior or methods from attached data sources.
User.attachTo(oracle);
console.log(User.availableMethods());
Output:
{
'User.all': {
accepts: [{arg: 'filter', type: 'object', description: '...'}],
returns: [{arg: 'users', type: ['User']}]
},
'User.find': {
accepts: [{arg: 'id', type: 'any'}],
returns: [{arg: 'items', type: 'User'}]
},
...
}
Data Source
An Asteroid DataSource
provides Models with the ability to manipulate data. Attaching a DataSource
to a Model
adds instance methods and static methods to the Model
. The added methods may be remote methods.
Define a data source for persisting models.
var oracle = asteroid.createDataSource({
adapter: 'oracle',
host: '111.22.333.44',
database: 'MYDB',
username: 'username',
password: 'password'
});
dataSource.createModel(name, options, settings)
Define a model and attach it to a DataSource
.
var Color = oracle.createModel('color', {name: String});
dataSource.discover(options, fn)
Discover an object containing properties and settings for an existing data source.
oracle.discover({owner: 'MYORG'}, function(err, tables) {
var productSchema = tables.PRODUCTS;
var ProductModel = oracle.createModel('product', productSchema.properties, productSchema.settings);
});
dataSource.discoverSync(options)
Synchronously discover an object containing properties and settings for an existing data source tables or collections.
var tables = oracle.discover({owner: 'MYORG'});
var productSchema = tables.PRODUCTS;
var ProductModel = oracle.createModel('product', productSchema.properties, productSchema.settings);
dataSource.discoverModels(options, fn)
Discover a set of models based on tables or collections in a data source.
oracle.discoverModels({owner: 'MYORG'}, function(err, models) {
var ProductModel = models.Product;
});
Note: The models
will contain all properties and settings discovered from the data source. It will also automatically discover and create relationships.
dataSource.discoverModelsSync(options)
Synchronously Discover a set of models based on tables or collections in a data source.
var models = oracle.discoverModels({owner: 'MYORG'});
var ProductModel = models.Product;
GeoPoint
Embed a latitude / longitude point in a 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.
CoffeeShop.attach(oracle);
var here = new GeoPoint({lat: 10.32424, long: 5.84978});
CoffeeShop.all({where: {location: {near: here}}}, function(err, nearbyShops) {
console.info(nearbyShops); // [CoffeeShop, ...]
});
geoPoint.distanceTo(geoPoint, options)
Get the distance to another GeoPoint
.
var here = new GeoPoint({lat: 10, long: 10});
var there = new GeoPoint({lat: 5, long: 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
- `miles`
- `radians`
- `kilometers`
geoPoint.lat
The latitude point in degrees. Range: -90 to 90.
geoPoint.long
The longitude point in degrees. Range: -180 to 180.
Asteroid Types
Various APIs in Asteroid accept type descriptions (eg. remote methods, asteroid.createModel()). The following is a list of supported types.
null
- JSON nullBoolean
- JSON booleanNumber
- JSON numberString
- JSON stringObject
- JSON objectArray
- JSON arrayDate
- a JavaScript date objectBuffer
- a node.js Buffer object- GeoPoint - an asteroid GeoPoint object.