Modules should rely on services to get or save data. Access to these services SHOULD be via an o-io
service to allow the product the ability to override or extend all (or part) of the module's strategy for doing I/O.
A module should NOT:
- use XMLHttpRequest, localStorage, cookies or IndexDB
- use a library such as $.ajax or superagent directly
- care whether something is stored locally or remotely
- should have no knowledge of how the product intends to provide this service to your module
You should assume all I/O operations are asynchronous.
It's your business where modules get and persist data.
You are responsible for defining the plumbing needed to back the services modules need. Where there is no server providing this service you need to create a micro-service in the browser. You do this via o-io
's Router.
o-io
provides a number of middleware functions to do the most common tasks, greatly reducing the work needed to route service requests to the correct destination. You need only configure the middleware and define specific logic needed for you app.
The provided middleware can do things like:
- get some JSON from a service endpoint via AJAX
- put something user data in localStorage
- ??
Examples of custom middleware:
- load from the server only if it's not already in IndexDB
- use cookies for legacy parts of your product, otherwise use localStorage
- decode/transform custom data formats as data arrives in the browser, centralise this logic and let modules have the decoded data
- offline images: modules just get the image data, not the specially encoded format we use on webapp.
- backoff
- monitoring and error tracking for service requests. do this in one place for the whole product
- handle negotiating disk space with the user, so modules don't have to.
- rewrite URLs in dev and test environments.
- product wants to show the UI in a loading state when waiting for any data from a particular (or all) services.
Module code
var io = require('o-io');
var service = io('//user.prefs/v1');
service.get('/articles/:id')
.param('id', 123).end()
.then(function(data){
// use the data
});
var userArticlePrefs = {foo:'bar'};
service.put('/article/:id')
.param('id', 123).data(userArticlePrefs).end()
.then(function(){
// do stuff
});
Product code
var io = require('o-io');
var middleware = require('o-io/middleware');
var router = io.router('//user.prefs/v1');
// middleware for a whole service or pattern of URIs
router.use(function(req, res, next) {});
// use one of the premade middleware - this one
// uses localstorage for persistance
router.use(middlware.localStorage({
// namespace all keys
namespace: 'userprefs'
});
// constrain a middleware to a specific verb
router.use('/article/*', 'GET', function(req, res, next) {
// modify the the get response in some way.
next();
});
Another (really lazy) product. This product is not backed by a server (offline only).
var io = require('o-io');
var middleware = require('o-io/middleware');
var router = io.router(); // when no service name specified then middleware are applied to all requests
// use local storage for everything
router.use(middleware.localstorage);
To create a service object...
var io = require('o-io');
var service = io('//user.prefs'); // the service names/rootURL
or
var io = require('o-io');
var options = { secure: true };
var service = io('//user.prefs', options); // see below for options
options:
params
: to add an API keys to every request, for example.secure
: use https. type:boolean. defaultfalse
- ???
One you have created a service object. You can call it's methods...
var service = io('//myservice.com/');
var request = service.get('/thingy/:id');
service.VERB
Returns a new Request
.
get(path)
get(path, options)
put(path)
put(path, options)
post(path)
post(path, options)
del(path)
del(path, options)
service.use
use(verb, middlewareFn)
tap into all requests for a given verb. Middleware signature: *req
: ??? *res
: ??? *next
: this param is optional. next is a function. use it to control the chain of middleware.use(middlewareFn)
tap into all requests for this service.
service.param
-
param(name, value)
-
param(name, fn)
sets para or middleware used for all requestsvar service = io('//myservice.com'); var getCurrentUser = function(){}; service.param('userId', getCurrentUser); serive.get('/users/:userId').end().then();
A request is not sent until end()
is called. All other methods all you to configure the request.
#####param
Set param tokens in path.
Return the Request
for chaining.
param(name, value)
param(name, fn)
param(object)
#####set
Set HTTP headers.
Return the Request
for chaining.
set(name, value)
set(name, fn)
set(object)
#####data
For PUT and POST sets the body of the request. For GET set the query params.
Return the Request
for chaining.
data(name, value)
data(obj)
data(fn)
this is equivalent to superagent's send
method. This method called be called send
or data
. The former is too similar to end
.
#####when
Delay sending the request until some other promises have been resolved/rejected.
Return the Request
for chaining.
when(promise)
orwhen([promise, promise, promise])
: postpones sending the requestend()
are complete.
#####end
Send the request. If when has been called on the request the send will also be delay until after those.
Returns a CommonJS promise.
end()
this method could instead by called send.
To create a Router object...
var io = require('o-io');
var router = io.router(); // no params. a router for all services
router.use(middlewareFn) // this middleware will apply to all services
or
var io = require('o-io');
var router = io.router('//user.*'); // routing for all service names matching this pattern
router.use(middlewareFn) // this middleware will only apply to requests on the matched services
use(middlewareFn)
use(pathExpression, middlewareFn)
use(pathExpression, verb, middlewareFn)
- ... more?
These will help with most tasks.
Use Superstore underneath.
options
- ?
- ?
- ?
Say what it does.
options
useCookieFallback
: boolean, default truenamespace
Sat what it does
options
- ?
- ?
Sat what it does
options
- db
- ?
I'm not sure if router is the right word. Could use
tap
?