58
Building a Node.js Client for Your REST+JSON API Les Hazlewood @lhazlewood CTO, Stormpath stormpath.com

Build a Node.js Client for Your REST+JSON API

Embed Size (px)

DESCRIPTION

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.

Citation preview

Page 1: Build a Node.js Client for Your REST+JSON API

Building a Node.js Clientfor Your REST+JSON API

Les Hazlewood @lhazlewoodCTO, Stormpath stormpath.com

Page 2: Build a Node.js Client for Your REST+JSON API

.com• User Management and Authentication

API• Security for your applications• User security workflows• Security best practices• Developer tools, SDKs, libraries

Page 3: Build a Node.js Client for Your REST+JSON API

Overview• Resources• Public / Private API• Proxy Design• Active Record• Fluent API• Configuration• Caching• Authentication• Pluggability• Lessons Learned

Page 4: Build a Node.js Client for Your REST+JSON API

HATEOAS

• Hypermedia

• As

• The

• Engine

• Of

• Application

• State

Learn more at Stormpath.com

Page 5: Build a Node.js Client for Your REST+JSON API

Resources

Learn more at Stormpath.com

Page 6: Build a Node.js Client for Your REST+JSON API

Resources

• Nouns, not verbs• Coarse-grained, not fine-grained• Support many use cases• Globally unique HREF

Learn more at Stormpath.com

Page 7: Build a Node.js Client for Your REST+JSON API

Collection Resource

• Example: /applications

• First class resource w/ own properties:• offset• limit• items• first, next, previous, last• etc

• items contains instance resources

Learn more at Stormpath.com

Page 8: Build a Node.js Client for Your REST+JSON API

Instance Resource

• Example:/applications/8sZxUoExA30mP74

• Child of a collection• RUD (no Create - done via parent collection)

Learn more at Stormpath.com

Page 9: Build a Node.js Client for Your REST+JSON API

Translating to Code

Learn more at Stormpath.com

Page 10: Build a Node.js Client for Your REST+JSON API

Resource

var util = require('util');

function Resource(...) { ... }util.inherits(Resource, Object);

someResource.href

Learn more at Stormpath.com

Page 11: Build a Node.js Client for Your REST+JSON API

Instance Resourcefunction InstanceResource(...) {...}util.inherits(InstanceResource, Resource);

anInstanceResource.save(function (err, saved) { ...});

anInstanceResource.delete(function (err) { ...});

Learn more at Stormpath.com

Page 12: Build a Node.js Client for Your REST+JSON API

Collection Resourcefunction CollectionResource(...) {...}util.inherits(CollectionResource, Resource);

aCollResource.each(function (item, callback) { ...}, function onCompletion(err) { ... });

aCollResource.eachSeriesaCollResource.mapaCollResource.filter... other async.js methods ...

Learn more at Stormpath.com

Page 13: Build a Node.js Client for Your REST+JSON API

Example: ApplicationListapplications.each(function(app, callback){ console.log(app); callback();}, function finished(err) { if (err) console.log(‘Error: ‘ + err);});

Learn more at Stormpath.com

Page 14: Build a Node.js Client for Your REST+JSON API

Design!

Learn more at Stormpath.com

Page 15: Build a Node.js Client for Your REST+JSON API

Encapsulation

• Public API• Internal/Private Implementations• Extensions

• Allows for change w/ minimal impacthttp://semver.org

Learn more at Stormpath.com

Page 16: Build a Node.js Client for Your REST+JSON API

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

Page 17: Build a Node.js Client for Your REST+JSON API

Public API

Learn more at Stormpath.com

Page 18: Build a Node.js Client for Your REST+JSON API

Public API

• All non-@private functions/vars• Builder methods (method chaining)• Object literals (config) is public too!

Learn more at Stormpath.com

Page 19: Build a Node.js Client for Your REST+JSON API

Public prototypical OO Classes

• Client• ApiKey• Application• Directory• Account• Group• etc

Learn more at Stormpath.com

Page 20: Build a Node.js Client for Your REST+JSON API

Classes with static helper methodsClient client = Clients.builder() ... .build();

• Create multiple helper classesseparation of concerns

Learn more at Stormpath.com

Page 21: Build a Node.js Client for Your REST+JSON API

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

Page 22: Build a Node.js Client for Your REST+JSON API

Private API

• Implementations + SPI interfaces• Builder implementations• Implementation Plugins

Learn more at Stormpath.com

Page 23: Build a Node.js Client for Your REST+JSON API

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

Page 24: Build a Node.js Client for Your REST+JSON API

Resourcevar 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

Page 25: Build a Node.js Client for Your REST+JSON API

InstanceResourcevar 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

Page 26: Build a Node.js Client for Your REST+JSON API

Applicationvar 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

Page 27: Build a Node.js Client for Your REST+JSON API

Usage Paradigm

Learn more at Stormpath.com

Page 28: Build a Node.js Client for Your REST+JSON API

Account JSON Resource{ “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “givenName”: “Tony”, “surname”: “Stark”, …, “directory”: { “href”: “https://api.stormpath.com/v1/directories/g4h5i6” }}

Learn more at Stormpath.com

Page 29: Build a Node.js Client for Your REST+JSON API

Proxy PatternString href = “https://api.stormpath.com/v1/....”;

client.getAccount(href, function(err, acct) { if (err) throw err;

account.getDirectory(function(err, dir) { if (err) throw err; console.log(dir); });

});

Learn more at Stormpath.com

Page 30: Build a Node.js Client for Your REST+JSON API

Proxy Pattern

Learn more at Stormpath.com

Page 31: Build a Node.js Client for Your REST+JSON API

Component Architecture

Learn more at Stormpath.com

Page 32: Build a Node.js Client for Your REST+JSON API

Component Architectureaccount .save()

Learn more at Stormpath.com

Page 33: Build a Node.js Client for Your REST+JSON API

Component Architectureaccount .save()

DataStore

Learn more at Stormpath.com

Page 34: Build a Node.js Client for Your REST+JSON API

Component Architectureaccount .save()

CacheManager

DataStore

Learn more at Stormpath.com

Page 35: Build a Node.js Client for Your REST+JSON API

Component Architectureaccount .save()

CacheManager

DataStore

CacheCacheCache

Learn more at Stormpath.com

Page 36: Build a Node.js Client for Your REST+JSON API

Component Architectureaccount .save()

RequestExecutorCacheManager

DataStore

CacheCacheCache

Learn more at Stormpath.com

Page 37: Build a Node.js Client for Your REST+JSON API

Component Architectureaccount .save()

RequestExecutor

AuthenticationStrategy

CacheManager

DataStore

RequestAuthenticator

CacheCacheCache

Learn more at Stormpath.com

Page 38: Build a Node.js Client for Your REST+JSON API

Component Architectureaccount

API Server

.save()

RequestExecutor

AuthenticationStrategy

CacheManager

DataStore

RequestAuthenticator

CacheCacheCache

Learn more at Stormpath.com

Page 39: Build a Node.js Client for Your REST+JSON API

Component Architectureaccount

API Server

.save()

RequestExecutor ResourceFactory JSON Resource

AuthenticationStrategy

CacheManager

DataStore

RequestAuthenticator

CacheCacheCache

Learn more at Stormpath.com

Page 40: Build a Node.js Client for Your REST+JSON API

Caching

Learn more at Stormpath.com

Page 41: Build a Node.js Client for Your REST+JSON API

Cachingvar cache = cacheManager.getCache(regionName);

cache.ttl //time to livecache.tti //time to idlecache.get(href, function(err, obj) { ...});

Learn more at Stormpath.com

Page 42: Build a Node.js Client for Your REST+JSON API

Cachingclient.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

Page 43: Build a Node.js Client for Your REST+JSON API

Queries

Learn more at Stormpath.com

Page 44: Build a Node.js Client for Your REST+JSON API

Queriesaccount.getGroups(function(err,groups){...});//results in a request to://https://api.stormpath.com/v1/accounts/a1b2c3/groups

• What about query parameters?

Learn more at Stormpath.com

Page 45: Build a Node.js Client for Your REST+JSON API

Queriesaccount.getGroups( { name: ‘foo*’, description: ‘*test*’, orderBy: ‘name desc’, limit: 100 }, function onResult(err, groups) { ... });

//results in a request to:

https://api.stormpath.com/v1/accounts/a1b2c3/groups? name=foo*&description=*test*&orderBy=name%20desc&limit=100

Learn more at Stormpath.com

Page 46: Build a Node.js Client for Your REST+JSON API

Queries

Use a Fluent API!

Learn more at Stormpath.com

Page 47: Build a Node.js Client for Your REST+JSON API

Queriesaccount.getGroups().where() .name().startsWith(“foo”) .description().contains(“test”) .orderBy(“name”).desc() .limitTo(100));//results in a request to:

https://api.stormpath.com/v1/accounts/a1b2c3/groups? name=foo*&description=*test*&orderBy=name%20desc&limit=100

Learn more at Stormpath.com

Page 48: Build a Node.js Client for Your REST+JSON API

Authentication

Learn more at Stormpath.com

Page 49: Build a Node.js Client for Your REST+JSON API

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

Page 50: Build a Node.js Client for Your REST+JSON API

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

Page 51: Build a Node.js Client for Your REST+JSON API

Plugins

Learn more at Stormpath.com

Page 52: Build a Node.js Client for Your REST+JSON API

Plugins

• Plugins or Extensions module• One sub-module per plugin• Keep dependencies to a minimum

plugins/|- request/|- foo/

Learn more at Stormpath.com

Page 53: Build a Node.js Client for Your REST+JSON API

Lessons Learned

Learn more at Stormpath.com

Page 54: Build a Node.js Client for Your REST+JSON API

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

Page 55: Build a Node.js Client for Your REST+JSON API

Lessons Learned: Promisesvar promise = account.getGroups();

promise.then(function() { //called on success}, function() { //called on error}, function() { //called during progress});

Learn more at Stormpath.com

Page 56: Build a Node.js Client for Your REST+JSON API

Lessons Learned: async.jsasync.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

Page 57: Build a Node.js Client for Your REST+JSON API

Code

$ git clone https://github.com/stormpath/stormpath-sdk-node.git

$ cd stormpath-sdk-node

$ npm install$ grunt

Learn more at Stormpath.com

Page 58: Build a Node.js Client for Your REST+JSON API

Thank You!

[email protected]• Twitter: @lhazlewood• http://www.stormpath.com

Learn more at Stormpath.com