View on GitHub

spas

Super Proxy Asset Server

Download this project as a .zip file Download this project as a tar.gz file

Build Your Bundle

A bundle is a group of API requests that you want combined into a single response. Each API request is a bundle part. Generally you will have a bundle for each website you build, but you may choose to have bundles that are only called from certain pages. Since spas responses can be highly optimized, I prefer to bundle everything together and minimize the number of requests made by the client even though I may not need one particular API all the time. We send all the correct caching and expiration headers to the browser and we want to make the minimum number of requests.

Bundles are defined in js files. For spas instances the are installed globally the bundles are stored in the current working directory (read: The directory you were in when you launched spas). For local installs, the bundles are stored in the bundles directory in your spas folder. Here is the sample.js bundle that is generated when you run spas --create:

var spashttp = require("spas-http");

exports.sample = {
  "searchTweets": {
    "resource": spashttp.request,
    "params": {
      "url": "http://search.twitter.com/search.json",
      "q": "dtex",
      "lang": "en",
      "count": 100 
    },
    "cacheduration": 3600,
    "timeout": 500,
    "filter": {
      "results": [{
        "from_user": true,
        "text": true }]
    }
  },
  "searchMoreTweets": {
    "resource": spashttp.request,
    "params": {
      "url": "http://search.twitter.com/search.json",
      "q":"spas",
      "lang": "en" 
    },
    "cacheduration": 3600
  }
};

This bundle is called sample and has two bundle parts: searchTweets and searchMoreTweets. The module we are requiring is the generic spas http request module. spas-http is maintained in a separate repository and will handle most basic json requests. spas-http documentation can be found at http://dtex.github.com/spas-http

Each bundle is an object subordinate to your exports object. The bundle name should be unique otherwise your bundles will collide in Redis. The bundle's immediate children are API request objects (aka "Bundle Parts"). We have two in our sample, "searchTweets" and "searchMoreTweets".

resource: (object and method, required) — The method you wish to use to retrieve your data.
params: (object, required) — Parameters object that is passed to resource method
cacheduration: (seconds, optional, default: 3600) — Controls how long a request to an API will be cached by spas.
schedule: (string, optional) — In addition to building bundles on demand, spas can refresh bundles on a scheduled basis. The schedule string is a Cron formatted string. Be mindful of rate limiting imposed by various providers when setting this up.
timeout: (milliseconds, optional, default: none) — Controls how long spas will wait for a request to an API before serving up the cached version, even if it has expired. When this happens spas will initiate an asynchronous request to update the bundle so it will be there next time.
filter: (optional, default: none) — A map of the results object showing just the parts you want to keep.
cleanup: (optional, default: none) — A function that accepts one parameter (your filtered api response as json) and returns the same object. You can perform any manipulation you please on the API response before returning it.

More on the resource parameter

In this case we are using the "request" method in the "spas-http" API description. spashttp["request"] is, as its name implies, a straightforward request to a public API. Most http API's that do not require a recursive request can be accessed via this basic method. More information about this API description and method can be found at http://dtex.github.com/spas-http

More on the params parameter

The structure of the params object is dependent on the API description you are utilizing. Most basic requests' params objects will consist of a url and a collection of key/value pairs to be passed as parameters in the query string in the request to the API.

More on the cachedurataion parameter

In the event that an API cannot be reached or is taking too long to respond, the cache will continue to be used until it can be refreshed asynchronously.

More on the timeout parameter

By default there is no timeout, so in the event that the API server does not properly close the connection you will never get a response from spas (not good). In the event that the timeout is reached, spas will return the most recently cached response and trigger a second request to the API that has no timeout and does not block the bundle response to your users. The returned bundle will be set to expire immediately and that second request should be available in the spas cache the next time your user makes a request. YouTube requests that do not use JSON-C are a good example of a request that can take a second or more. Also recursive requests can take a while (i.e. get a list of objects and then get details on each object). You never want users to wait that long.

More on the filter parameter

There's not much you can compress in a JSON response, but there may be a lot of stuff you don't need. Consider the "searchTweets" example above. All we care about is the text of the tweet and who sent it. By dropping the other stuff we reduce the size of the API response by over 75%.

The map should reflect the structure of the JSON returned by the API, using the exact same keys and heirarchy. Properties and objects not included in the map will be discarded. A key with the value of "true" will cause that property or object to be kept in its entirety. Any key may have and object that further describes the content within that should be kept. Array maps should have just one element in the array which is an object describing the elements to keep within the array element.

More on the cleanup parameters

A cleanup parameter can be bound to an API response object or to the entire bundle. In the cleanup you can perform any kind of transform you see fit. My first use case was to take an API response object from Vimeo, transform it to match the YouTube format then concatenate and sort the two. This way my client side code could be written for a basic YouTube stream but I'd be interleaving YouTube and Vimeo feeds together.

Next: Use Your Bundle