-
Notifications
You must be signed in to change notification settings - Fork 3
The data apis in miso are a way to create a RESTful endpoint that you can interact with via an easy to use API.
Note: you must enable your api by adding it to the "api" attribute in the `/cfg/server.development.json` file, or whatever environment you are using.
The apis in miso do a number of things:
- Allow database access via a thin wrapper, for example to access mongodb, we wrap the popular mongoose npm ODM package
- Waits till mithril is ready - mithril has a unique feature ensures the view doesn't render till data has been retrieved - the api makes sure we adhere to this
- Apis can work as a proxy, so if you want to access a 3rd party service, an api is a good way to do that - you can then also build in caching, or any other features you may wish to add.
- Apis can be restricted by permissions (coming soon)
There are numerous scenarios where you should use an api:
- For database access (miso comes with a bunch of built-in database apis)
- For calling 3rd party end-points - using an api will allow you to create caching and setup permissions on the end-point
- Any other data that needs to be accessed outside of the DOM
If you want to add your own methods to an api, you can simply extend one of the existing apis, for example, to extend the flatfiledb API, create a new directory and file in /modules/api/adapt/adapt.api.js:
var db = require('../../../system/api/flatfiledb/flatfiledb.api.js');
module.exports = function(m){
var ad = db(m);
ad.hello = function(cb, err, args, req){
cb("world");
};
return ad;
};Then add the api to the /cfg/server.development.json file like so:
"api": "adapt"Then require the new api file in your mvc file like so:
db = require('../modules/api/adapt/api.server.js')(m);You can now add an api call in the controller like so:
db.hello({}).then(function(data){
// do something with data.result
});The arguments to each api endpoint must be the same, ie:
function(cb, err, args, req)Where:
| Argument | Purpose |
|---|---|
| cb | A callback you must call when you are done - any data you return will be available on data.result in the response |
| err | A callback you must call if an unrecoverable error occurred, eg: "database connection timeout". Do not use for things like "no data found" |
| args | A set of arguments passed in to the api method |
| req | The request object from the request |
The complete mvc example looks like so:
var m = require('mithril'),
miso = require('../server/miso.util.js'),
sugartags = require('mithril.sugartags')(m),
db = require('../modules/api/adapt/api.server.js')(m);
var edit = module.exports.edit = {
models: {
hello: function(data){
this.who = m.prop(data.who);
}
},
controller: function(params) {
var ctrl = this,
who = miso.getParam('adapt_id', params);
db.hello({}).then(function(data){
ctrl.model.who(data.result);
});
ctrl.model = new edit.models.hello({who: who});
return ctrl;
},
view: function(ctrl) {
with(sugartags) {
return DIV("G'day " + ctrl.model.who());
}
}
};All data requests will block rendering by default, this is a feature of mithril - it ensures that your data is always loaded before you try to use it. So if your controller loads data on startup, the page won't render till your data has loaded, and this behaviour is also built into the mis APIs. Of course you might not need the data for your page to render, (eg: you can have a reasonable default value, load data, and then update your model), so in order to avoid the blocking behaviour, simply use the 2nd parameter for each API call - any arguments in this object will be passed to m.request, eg:
// Load some pictures
flickr.photos({
tags: "Sydney opera house",
tagmode: "any"
}, {
background: true,
initialValue: []
}).then(...);This ensures your page renders with the initial value, and you can then manipulate the returned data, and populate your model, triggering an update in your view automatically.
You can add your own custom apis in the /modules/apis directory, they have the same format as the included apis, here is an example api that calls the flickr API:
// endpoint api to make http requests via flickr
var request = require('request'),
miso = require('../../../server/miso.util.js'),
// Parse out the unwanted parts of the json
// typically this would be run on the client
// we run this using "request" on the server, so
// no need for the jsonp callback
jsonParser = function(jsonpData){
var json, startPos, endPos;
try {
startPos = jsonpData.indexOf('({');
endPos = jsonpData.lastIndexOf('})');
json = jsonpData
.substring(startPos+1, endPos+1)
.split("\n").join("")
.split("\\'").join("'");
return JSON.parse(json);
} catch(ex) {
console.log("ERROR", ex);
return "{}";
}
};
module.exports = function(utils){
return {
photos: function(cb, err, args, req){
args = args || {};
var url = "http://api.flickr.com/services/feeds/photos_public.gne?format=json";
// Add parameters
url += miso.each(args, function(value, key){
return "&" + key + "=" + value;
});
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
cb(jsonParser(body));
} else {
err(error);
}
});
}
};
};To use it in your mvc file, simply:
flickr = require('../modules/api/flickr/api.server.js')(m);And then call it like so in your controller:
flickr.photos({tags: "Sydney opera house", tagmode: "any"}).then(function(data){
ctrl.model.flickrData(data.result.items);
});