README
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 smidig
Using 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 below
Storing values
The smidig.set
function is used to store both values and functions.
smidig.set( where, what, whereContext )
It expects three arguments:
where
is an absolute path like/foo/bar
.where
can also be afunction
which is executed and whose return value will be used as a path if it is neitherundefined
nornull
nor a function.what
is the value to set.- Optional:
whereContext
is anArray
with 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:
where
can be a path or a function that will be executed and whose return value will be used as the path.A context given as
this
to the function stored in the API (if any)A optional
boolean
indicating 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.598150033144236
Using 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 here
Longer 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) ); // -4
Building 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.