<script type="text/javascript" src="https://cdn.jsdelivr.net/es6-promise/3.1.2/es6-promise.min.js"></script> <!-- older browsers -->
<script type="text/javascript" src="dist/restglue.min.js"></script>
or in nodejs just do
npm install restglue superagent
and then:
var restglue = require('restglue')
var myapi = new restglue()
myapi.headers['Content-Type'] = 'application/json'
myapi.addEndpoint("pizza")
Not really exciting yet, but now you can do calls like so:
myapi.pizza.getAll()
.then( function(json){ // json-result of GET /pizza
// call json.getResponse() for raw response (headers etc)
})
.catch( function(err){
console.log("could not get pizza")
})
NOTE: use
new restglue("http://api.foo.com/v1")
to automatically prepend an external apiurl to all endpoints, and make sure you got CORS setup on your server when doing requests from the browser.
getAll(query, headers) - will do GET /pizza
post(payload, query, headers) - will do POST /pizza {..}
get(id, query, headers) - will do GET /pizza/{id}
put(id, payload, query, headers) - will do PUT /pizza/{id} {..}
delete(id, payload, query, headers) - will do DELETE /pizza/{id} {..}
patch(id, payload, query, headers) - will do PATCH /pizza/{id} {..}
options(id, payload, querym headers) - will do OPTIONS /pizza/{id} {..}
NOTE:
query
andheaders
are optional and are used only for that request.
myapi.pizza.customPost = restglue.prototype.request.bind( this, "post", '/foo/bar', {payload:true}, {queryfoo:1, querybar:2}, {X-HEADER-FOO:12} )
myapi.pizza.customGet = restglue.prototype.request.bind( this, "get", '/foo/bar' )
Also, you can monkeypatch these function to alter restglue's behaviour:
restglue.prototype.addEndpointg( resourcename )
restglue.prototype.afterRequestg(cb)
restglue.prototype.beforeRequestg(cb)
restglue.prototype.composeg(chain)
restglue.prototype.constructorg(apiurl)
restglue.prototype.getSandboxedUrlg(method,url)
restglue.prototype.requestg(method, url, payload, query, headers)
restglue.prototype.sandboxUrlg(url,destination)
restglue.prototype.toQueryStringg(data)
You can fake responses (for offline development etc) in 2 ways, like so:
myapi.addEndpoint("foobar")
myapi.addEndpoint("foo")
myapi.sandboxUrl('/foobar', {'data':{"foo":true}} )
myapi.sandboxUrl('/myapi', {'path':"/js/sandbox"} )
myapi.sandboxUrl( /some.*regex/, "/js/foo" )
myapi.foobar.getAll().then(function(data){
// data = {"foo":true}
})
myapi.foo.getAll().then(function(data){
// data = /js/sandbox/foo/get.json instead of GET {apiurl}/myapi/foo
})
NOTE: {apiurl} is passed using
new restglue({apiurl:"http://foo.com/v1"})
Byebye async spaghetti, welcome clean code.
Combine multiple endpoints into one call:
myapi.pizza.getCookPageRanking = myapi.compose([
function(i) { return myapi.pizza.getAll({"sort":"-date_create"}) },
function(res){ return otherapi.getRanking(res.cook.profile_url) },
function(res){ return res.score }
])("foo")
myapi.pizza.getCookPageRanking().then( function(res){
// res is '4'
}).catch( function(err){ .. })
myapi.pizza.getAll( {"sort":"-date_create"} )
.then( function(res){
// result of GET /pizza?sort=-date_create
}
var password = "bar"
myapi.headers['Authorization'] = 'Basic '+btoa( login+":"+password )
// do calls
myapi.pizza.getAll()
.then( function(res){
var headers = res.getResponse().headers
})
NOTE: Make sure you have CORS configured properly on your server, otherwise certain headers won't be accessible in javascript.
beforeRequest
and afterRequest
allow you to massage the request or response
myapi.beforeRequest( function(config){
// format the input for an exotic api, before doing the actual request
config.payload = { type: "payload", payload:config.payload }
})
Here's how to simply prevent unnecessary calls
var cache = {get:{}}
myapi.beforeRequest( function(config){
if( config.method == "get" && cache.get[config.url] ) return cache.get[config.url]
})
myapi.afterRequest( function(config, res, err){
if( config.method == "get" && !err ) cache.get[ config.url ] = res
})
NOTE: optionally you can store a
new Date().getTime()
timestamp, and bypass the cache when expired
This easifies iterative, backwardscompatible development:
function getApi(){
var v1 = new restglue("http://api.foo.com/v1"),
var v2 = new restglue("http://api.foo.com/v2"),
var api = {
ga: new restglue("https://www.googleapis.com/analytics/v3")
}
// *TODO* call addEndpoint(...) on v1,v2 and googleanalytics
// ok, we're assuming the v1 and v2 endpoints are setup
// so now we set v1 endpoints as default
for( i in v1 ) api[i] = v1[i]
// but upgrade the pizza endpoint to v2
api.pizza = v2.pizza
return api
}
var myapi = getApi()
var login = "foo"
var password = "bar"
myapi.addEndpoint("user/current")
myapi.headers['Authorization'] = 'Basic '+btoa( login+":"+password
myapi.pizza.getAll()
.then( function(res){
// authenticated response
})
.catch( function(err){
console.log(err)
})
var login = "foo"
var password = "bar"
myapi.addEndpoint("user/current")
myapi['user/current'].getAll(false, { 'Authorization': 'Basic '+btoa( login+":"+password ) })
.then( function(res){
if( ! res.bearer_hash ) throw new Exception("AUTH_FAILED")
myapi.headers['Authentication'] = "bearer "+res.bearer_hash
})
.catch( function(err){
console.log(err)
})
Eventhough I prefer fetch, this module relies on superagent and not on fetch because:
- I had some weird experiences with fetch-polyfill vs native fetch (I guess it needs a bit of time)
- XHR request seems a more robust choice before fetch really takes over
- superagent seems battletested and has a lot of extensions and plugins
Also i noticed projects like restful.js, frisbee, superagent-defaults, superagent-jsonapify,superagent-ls,superapi, superagent-pool, super-res
but I needed a little bit more and less, in short I need:
- a drop-in solution (not all js devs have ES6/ES7 transpilertoolchain-experience)
- easy chaining of endpoints, from multiple api's (hence async helpers included)
- promises
- requestpool cache but with TTL (hence the hooks, so the beforeRequest-hook can return a cached response)
- compose a superapi (swap around endpoint versions)