In this presentation, Les Hazlewood - Stormpath CTO and Apache Shiro PMC Chair - will share all of the golden nuggets learned while designing, implementing and supporting a Node.js Client purpose-built for a real-world REST+JSON API.
Further reading: http://www.stormpath.com/blog
Stormpath is a user management and authentication service for developers. By offloading user management and authentication to Stormpath, developers can bring applications to market faster, reduce development costs, and protect their users. Easy and secure, the flexible cloud service can manage millions of users with a scalable pricing model.
Russian Call Girls in Karol Bagh Aasnvi âĄď¸ 8264348440 đđ Independent Escort S...
Â
Build a Node.js Client for Your REST+JSON API
1. Building a Node.js Client
for Your REST+JSON API
Les Hazlewood @lhazlewood
CTO, Stormpath stormpath.com
2. .com
⢠User Management and Authentication
API
⢠Security for your applications
⢠User security workflows
⢠Security best practices
⢠Developer tools, SDKs, libraries
3. Overview
⢠Resources
⢠Public / Private API
⢠Proxy Design
⢠Active Record
⢠Fluent API
⢠Configuration
⢠Caching
⢠Authentication
⢠Pluggability
⢠Lessons Learned
15. Encapsulation
⢠Public API
⢠Internal/Private Implementations
⢠Extensions
⢠Allows for change w/ minimal impact
http://semver.org
Learn more at Stormpath.com
16. Encapsulation in practice
⢠Use an underscore prefix: _
⢠Super clear code comments:
mark @public or @private
⢠Public API docs: donât display private
classes/methods/functions
Learn more at Stormpath.com
18. Public API
⢠All non-@private functions/vars
⢠Builder methods (method chaining)
⢠Object literals (config) is public too!
Learn more at Stormpath.com
19. Public prototypical OO Classes
⢠Client
⢠ApiKey
⢠Application
⢠Directory
⢠Account
⢠Group
⢠etc
Learn more at Stormpath.com
20. Classes with static helper methods
Client client = Clients.builder()
...
.build();
⢠Create multiple helper classes
separation of concerns
Learn more at Stormpath.com
21. Builder methods (method chaining)
client.getApplications()
.where(name).startsWith(â*fooâ)
.orderBy(name).asc()
.limit(10)
.execute(function (err, apps){
...
});
clients.getApplications() ď ApplicationRequestBuilder
Single Responsibility Principle!
Learn more at Stormpath.com
22. Private API
⢠Implementations + SPI interfaces
⢠Builder implementations
⢠Implementation Plugins
Learn more at Stormpath.com
23. Resource Implementations
⢠Create a base Resource class:
⢠Property manipulation methods
⢠Dirty checking
⢠Reference to DataStore
⢠Lazy Loading
⢠Create base InstanceResource and CollectionResource
implementations
⢠Extend from InstanceResource or CollectionResource
Learn more at Stormpath.com
24. Resource
var utils = require(âutils');
function Resource(data, dataStore) {
var DataStore = require('../ds/DataStore');
if (!dataStore && data instanceof DataStore){
dataStore = data;
data = null;
}
data = data || {};
for (var key in data) {
if (data.hasOwnProperty(key)) {
this[key] = data[key];
}
}
var ds = null; //private var, not enumerable
Object.defineProperty(this, 'dataStore', {
get: function getDataStore() {
return ds;
},
set: function setDataStore(dataStore) {
ds = dataStore;
}
});
if (dataStore) {
this.dataStore = dataStore;
}
}
utils.inherits(Resource, Object);
module.exports = Resource;
Learn more at Stormpath.com
25. InstanceResource
var utils = require(âutils');
var Resource = require('./Resource');
function InstanceResource() {
InstanceResource.super_.apply(this, arguments);
}
utils.inherits(InstanceResource, Resource);
InstanceResource.prototype.save = function
saveResource(callback) {
this.dataStore.saveResource(this, callback);
};
InstanceResource.prototype.delete = function
deleteResource(callback) {
this.dataStore.deleteResource(this, callback);
};
Learn more at Stormpath.com
26. Application
var utils = require(âutils');
var InstanceResource = require('./InstanceResource');
function Application() {
Application.super_.apply(this, arguments);
}
utils.inherits(Application, InstanceResource);
Application.prototype.getAccounts = function
getApplicationAccounts(/* [options,] callback */) {
var self = this;
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
var options = (args.length > 0) ? args.shift() : null;
return self.dataStore.getResource(self.accounts.href, options,
require('./Account'), callback);
};
Learn more at Stormpath.com
41. Caching
var cache = cacheManager.getCache(regionName);
cache.ttl //time to live
cache.tti //time to idle
cache.get(href, function(err, obj) {
...
});
Learn more at Stormpath.com
42. Caching
client.getAccount(href, function(err, acct) {...});
// in the DataStore:
var cache = cacheManager.getCache(âaccountsâ);
cache.get(href, function(err, entry) {
if (err) return callback(err);
if (entry) {
... omitted for brevity ...
return callback(entry.value);
}
//otherwise, cache miss â execute a request:
requestExecutor.get(href, function(err, body) {
//1. cache body
//2. convert to Resource instance
//3. invoke callback w/ instance
}
}
Learn more at Stormpath.com
49. Authentication
⢠Favor a digest algorithm over HTTP Basic
⢠Prevents Man-in-the-Middle attacks (SSL wonât guarantee
this!)
⢠Also support Basic for environments that require it (Dammit
Google!)
⢠ONLY use Basic over SSL
⢠Represent this as an AuthenticationScheme to your Client /
RequestExecutor
Learn more at Stormpath.com
50. Authentication
⢠AuthenticationScheme.SAUTHC1
⢠AuthenticationScheme.BASIC
⢠AuthenticationScheme.OAUTH10a
⢠... etc ...
Client client = new stormpath.Client({
//defaults to sauthc1
authcScheme: âbasicâ
});
Client/RequestExecutor uses a Sauthc1RequestAuthenticator or
BasicRequestAuthenticator or OAuth10aRequestAuthenticator, etc.
Learn more at Stormpath.com
52. Plugins
⢠Plugins or Extensions module
⢠One sub-module per plugin
⢠Keep dependencies to a minimum
plugins/
|- request/
|- foo/
Learn more at Stormpath.com
54. Lessons Learned
⢠Recursive caching if you support resource
expansion
⢠Dirty checking logic is not too hard, but it does
add complexity. Start off without it.
Learn more at Stormpath.com
55. Lessons Learned: Promises
var promise = account.getGroups();
promise.then(function() {
//called on success
}, function() {
//called on error
}, function() {
//called during progress
});
Learn more at Stormpath.com
56. Lessons Learned: async.js
async.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
Learn more at Stormpath.com