smidig v0.0.5
smidig
smidig is a simple, small and flexible framework that provides a container to build APIs for your application. It allows you to store both values and functions identified with a unique path.
Installation
smidig can be installed using npm:
npm install smidigUsing require() the package can be imported. The returned function has to be invoked to create a new
API storage. This allows you to use multiple separated instances of it.
var smidig = require("smidig");
var myAPI = smidig();
myAPI.set( .... ); // Set a value; see below
myAPI.get( .... ); // Get a value; see belowStoring values
The smidig.set function is used to store both values and functions.
smidig.set( where, what, whereContext )It expects three arguments:
whereis an absolute path like/foo/bar.wherecan also be afunctionwhich is executed and whose return value will be used as a path if it is neitherundefinednornullnor a function.whatis the value to set.- Optional:
whereContextis anArraywith arguments passed to the functionwhere.
Although you can provide an array of arguments, the first argument to where will always be what.
Here is an example that demonstrates the setter function:
var smidig = require("smidig")();
function genPath(value,prefix) {
return "/"+prefix+"/"+value;
}
smidig.set(genPath, 'foobar', ['_pref'] );
smidig.set("/hello/world","hello, world");The function genPath is used to generate the path for the API. It arguments
are the value to set ("foobar") and a prefix ("_pref"). It will generate
the path "_pref/foobar" which will contain the value "foobar".
Deleting values
The smidig.unset function can be used to remove values from the API storage.
smidig.unset( where )As with smidig.set it is possible to provide a function that will be executed
and whose return value will be used as a path.
smidig.unset('/hello/world');
var func = function() { return '/delete/me'; };
smidig.unset( func );Retrieving values
smidig.get is used to get values from the API. At this point it becomes relevant if you have stored
a function or a value. Functions can be executed and their return value will be returned.
smidig.get( where, context, executeFunctions )The three arguments:
wherecan be a path or a function that will be executed and whose return value will be used as the path.- A context given as
thisto the function stored in the API (if any) - A optional
booleanindicating if stored functions should be executed. Default value istrue.
smidig.get('/foo/bar');
smidig.get('/a/function', ['arg1','arg2']);Direct access
Although the smidig.get function allows you to access stored values, the hierarchical structure of the API let
you access your paths directly without calling a function:
var smidig = require("smidig");
var myAPI = smidig();
myAPI.set('/math/calc/exp',Math.exp);
myAPI.api.math.calc.exp(4); // 54.598150033144236Using the .api property you can access all the stored properties without the overhead of a function.
This way, you have both the ability to use a direct ("static") access to your API and the ability to access it with a
string that might be given using another framework like express.
The .api property is a read-only attribute that cannot be changed.
var storage = smidig();
console.log( storage.api ); // { }
storage.api = "changed?";
console.log( storage.api ); // { } - no change hereLonger example
Here is an example demonstrating how smidig can be used:
var smidig = require("smidig");
var firstAPI = smidig();
var secondAPI = smidig();
function generatePrivatePath( value, name, keys ) {
var path = "/_private/";
if( 'string' === typeof keys && '' !== keys.trim() )
path += keys + "/";
return path + name;
}
function storeFunction( fn ) {
return '/fn/'+fn.name;
}
// Store some functions
firstAPI.set(storeFunction, Math.max); // stored as /fn/max
secondAPI.set(storeFunction, Math.min);
// Some 'private' keys
firstAPI.set( generatePrivatePath, 'password123', ['password','users/johndoe']);
// Let's get all the things we have stored
console.log("Password for johndoe:", firstAPI.get(generatePrivatePath,[],['password','users/johndoe']) ); // password123
console.log("/fn/max for 4,5 is ", firstAPI.get('/fn/max', [4,5] )); // 5
console.log("/fn/min for 99,41,-4 is ", secondAPI.get('/fn/min',[99,41,-4])); // -4
console.log("/fn/sqrt for 1024 is ", firstAPI.get('/fn/sqrt', [1024] )); // undefined
// More flexibility
console.log("Good usage example result:", secondAPI.api.fn.min(99,41,-4) ); // -4Building RESTful APIs with express
smidig comes with an express middleware function that allows you to build RESTful APIs with ease. The express integration is not limited to HTTP access but set up and accessed as described above. Thus, you can allow clients to use the same API you are working with on your server.
Registering the handler
smidig provides a function that returns an express middleware function. A middleware function is invoked before
a function registered with express.VERB (e.g. express.post(...)).
smidig.express( apiPrefix, apiObject, functionContext )The smidig API middleware function will handle every request whose request path begins with
the given prefix (first argument: apiPrefix). Other requests are ignored and have to be handled somewhere else.
Pass your smidig API object as the second parameter.
An optional function context can be given as a third argument. This context will be passed to functions
stored in the API as this. The default context contains the api and the api path prefix as well as
request related information (see below).
defaultContext = {
api: API_object,
target: path_prefix
};Function contexts
Both the default function context as well as a user defined context are extended w/ request information
such as the path (relative to target) and query information. These properties will be overriden
without checking for existence, keep that in mind when using this feature.
injectedProperties = {
path: relative_request_path,
query: GET_parameters,
body: POST_body
};Thus, the minimal this context passed to functions stored in the API looks like this:
this = {
api: API_object, // Only in default context
baseurl: path_prexix, // Only in default context
path: relative_path, // always
query: GET_params, // always
body: POST_params, // always
};Return values
Functions are be executed and they return value is serialized. To provide valid JSON responses
the API has default JSON for strings, numbers, booleans and functions as well as undefined and null.
If a function returns an object or an object is stored in the API, it is serialized with JSON.stringify
and send to the client. Thus, you have to make sure that your object can be converted into a string.
/* String */
{ type: 'string-result', result: your_string }
/* Boolean */
{ type: 'boolean-result', result: your_boolean }
/* Number */
{ type: 'number-result', result: your_number }
/* Function */
{ type: 'function-result', result: your_fn_as_string }
/* undefined */
{ type: 'smidig-result', result: 'undefined' }
/* null */
{ type: 'smidig-result', result: 'null' }Example
Here is a working example for a simple API:
var smidig = require("smidig");
var express = require("express");
var app = express();
var api = smidig();
api.set('/fn/max', function(){
// Will always return 4
return Math.max(3,4);
});
api.set('/hello', function(){
return {
message: 'hello from ' + this.path
};
});
// Return number 42
api.set('/number/42', 42);
/*
* Accessing this function will return
*
* {"fn":{},"number":{"42":42}}
*
* in the backend and
*
* {"target":"/api/","api":{},"path":"/context","query":{}}
*
* when using an HTTP client
*/
api.set('/context', function(){
// REST?
if( this.path )
return this;
// Backend access
else
return JSON.stringify(this);
});
console.log( api.api.context() ); // {"fn":{},"number":{"42":42}}
// Setup the handler with default context
app.use( smidig.express('/api/', api ) );
// Handle all other requests
app.all('*', function(req,res){
res.end('Handler did not handle the request');
});
app.listen(1337);The API can be accessed with a web browser at http://localhost:1337/api/api_path.
Requesting http://localhost:1337/api/context will print the this context. A good
point to start with smidig's context mechanism.