Hi Ryan,
You're right - these are badly in need of documentation. Hopefully we'll fill that gap soon.
You can really put anything you like in those stages - it's basically async sugar for rendering views, with some built in cleverness for querying data and handling different HTTP methods conditionally.
So init is for anything you need in every case, e.g.
view.on('init', function(next) { /* do something async and required regardless of actions */ })
Actions are shortcut methods for handling events like form posts, optionally with body values that are checked (e.g. if you have two forms on a page, include an action hidden input in each with different values)
view.on('post', { action: 'submit-form' }, function(next) { /* handle the form submission */ })
There's sugar for executing queries, which happen after actions (since you may change data with an action that would affect the query return)
view.query('key', SomeList.model.find().where('condition').eq('value'))
is basically equivalent to:
view.on('render', function(next) {
SomeList.model.find().where('condition').eq('value').exec(function(err, results) {
locals.key = results || [];
next(err);
});
});
... and the render queue happens last before the view is rendered.
Speaking of which, yes - you can pass the render method a function (to respond with JSON from an API) which will be executed finally, instead of the name of a template. So yes it works well for REST APIs.
view.render(function(err) {
if (err) return res.send({ err: err });
res.send({ results: locals.key }); // see above :)
});