Introduction
This article is the 2nd part of a previous article... but is not written with contextual reference to the 1st part. This can be considered as an article on customizing sails API generate, for the Sails Nodejs framework.
Reference: Sails API development (1/2): Datalayer -models, connections, waterline, API -blueprint, and related
What is covered in the article:
- You have generated API through sails, and attached models, modified the models.
- We learn about making the API fit into your requirements, namely customizing it, modifying default behaviour.
How to Add Custom Methods / Logic on Your Sails API
Default / Shadow Routes
By default, sails provides the following methods, more details here.
The current version of Sails ships with the following API methods / default blueprint actions:
find
findOne
create
update
destroy
populate
add
remove
That means, only for the above method calls, your API routes are defined (these default routes are called 'shadow routes' on the blueprint framework.)
To customize what happens when the above methods are called, or to validate input data on API calls, or to override or disable these API end points, we have to customize the default actions/routes, or add new actions.
Customization Techniques Covered
We will explore four customization techniques targeted for sails v0.10.5 below:
Customize Existing API Action/Route
Say, our database has a guid primary key, and we want to check that the call to our API passes a guid parameter.
If not passed, or if we want to generate a guid, we need to add code customization like below.
All shadow route / default API call implementations, are defined under your sails folder \lib\hooks\blueprints\actions (and your sails folder is under your node_modules folder.)
We open the create.js under the directory, and just after getting the request parameters, we add additional code.
/lib/hooks/blueprints/actions/create.js
module.exports = function createRecord (req, res) {
var Model = actionUtil.parseModel(req);
var data = actionUtil.parseValues(req);
if(!data.guid) { data.guid = guid(); }
console.log(JSON.stringify(data));
...
At the end of create.js, we add the guid method (function guid code taken from http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript).
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
After the above changes, whenever you call a 'create
' action, you will notice your sails window showing the console log, and your model getting the guid parameter.
The above code is written for a model and API call like below:
API call:
http://localhost:1337/monster/create?name=lkjkljlkj
/api/models/monster.js
module.exports = {
attributes: {
name: {
type: 'string',
columnName: 'name'
},
guid: {
type: 'string',
unique: true,
primaryKey: true,
columnName: 'guid'
}
}
};
Override Existing API Action/Route
In a similar way like above, but without touching sails code under your node_modules\sails directory (so that you can upgrade sails later and your customization won't have to be re-written), you could do the customization by overriding the blueprint implementation.
(As suggested here: https://stackoverflow.com/questions/22273789/crud-blueprint-overriding-in-sails-js)
Create a \blueprints folder under your project's \api directory, and create a file like create.js if you want to override the default implementation.
And the hint is: ----------------
You could refer to / make a copy of the default create.js (as used in previous section) and modify.
It should be fine, except that, to ensure blueprint ActionUtil
reference works, you may have to add the below line in replacement of the require(actionUtil)
line:
actionUtil = require('sails/lib/hooks/blueprints/actionUtil');
-------------------
If your \api\blueprints directory contains only create.js, sails intelligently overrides only the create blueprint method, and not the others.
NOTE
- One problem though, is that, all overrides are for all API controllers. So if you have 2 APIs in your project like admin.js and user.js, then there is no way you can override admin.js's
destroy
method alone (as an example).
- So if you override destroy.js for one API controller, you are overriding it for all of them, and additional code may have to be written (read on to below point).
- This behaviour is by design.
- You can use
req.options.controller
/ req.options.model
in your overridden code, to decide how your override works for different controllers. These options will return for which model/controller the destroy is called.
- Currently all overrides related files must be lowercase! (The default actions contains findOne.js, but in /api/blueprints, it needs to be findone.js.)
Disable Existing API Action/Route
You could disable all default blueprint actions for your API by putting the below in your API's controller.
So if you had a monster API, then you would put the below code in /api/controllers/MonsterController.js.
The _config
values below, will override global configuration under /config/blueprint.js, for your controller.
module.exports = {
_config: {
actions: false,
shortcuts: false,
rest: false
}
}
The above disables all default actions. Mostly, you won't need to do that, since that kind of handicaps the necessity for any auto-generated API itself.
But if you want some API functionality to be secured or protected away from default, the below will help.
How to Disable Only Some of the Default Methods but Not All
Suppose you do not want your API users to delete records.
You could simply override the destroy
method by creating an empty destroy.js file under /api/blueprints/destroy.js.
And this will return a 404 for the destroy
call.
Note: Overrides apply for all controllers not just one of them, to control that behaviour, read the Note section under 'override existing' section above.
Add New API Action/Route
Again, all you need to do is add a custom method under /api/blueprints folder. If you add abc.js then abc
is loaded as an API method through blueprint as mentioned in 'override existing' section above.
Note: For the custom methods which are NOT overrides of the shadow routes, you have to add custom routes.
This is just an easy config line to be added in routes config (as found here).
If you create a /blueprints/foo.js file, you can bind a route to it
in your /config/routes.js file with (for example):
GET /myRoute': {blueprint: 'foo'}
Sidebar Note: There is an interesting conversation on a similar customization with the core contributors at https://github.com/balderdashy/sails/issues/1653.
Sails Missing Documentation - Some Found Recorded Under github Issues @ https://github.com/balderdashy/sails/issues
In fact, a lot of the missing documentation for sails can be learnt from the sails github issue threads... like hidden functions.
Some amount of detail in this article is based out of information consolidated from there.