apiextender
v3.1.4
Published
This module deals with to extend an API backend that adds a specific feature to an existing API as plugin(add-in, addin, add-on, addon). apiextender let to enable third-party developers to create abilities which extend an application and supports the a
Downloads
63
Maintainers
Readme
apiextender
This module allows to extend an API backend, by adding endpoints or modifying existing endpoints' behaviour. This is accomplished in a plug-in fashion. With apiextender you can :
- enable third-party developers to create functionalities extending an application
- support the addition of new features in a simple way
- Installation
- Using apiextender
- make your environment capable to accept plugins functions
- How to write function that extends the "API"
- Reference
- Examples
- Examples:How to set an API extensible by plugins
- Examples:How to write extensible plugin functions
- Examples:override mode
- Examples:before mode
- Examples:after mode
- Examples:before_after mode
- Examples:extend throwing error
- Examples:A Complete Example of plugin extension
- Example: Extend API on the fly at runtime
Installation
Install apiextender in your project by typing:
$ npm install apiextender
Usage
This is an Express module for nodeJs. This section explains how to use it in your code to make your API extensible by plugin.
Include apiextender
Just require it:
var apiextender = require('apiextender');
Using apiextender
var express=require('express');
var apiextender = require('apiextender');
var app=express();
// make your API extensible by plugin techniques in a simple and fast
// and with one line of code.
apiextender.extend(app); // now your API is exensible
Make your environment compliant to plugins
Plugin functions that extends your API must be defined in a file called extend.js
,
located in a folder named plugin
in the home directory of your application.
Create plugin folder
To create folder type:
$ mkdir plugin
Create plugin file container
To create extend.js
type:
$ cd plugin
$ vi extend.js
extend.js
contains an array of plugin functions. Now we initialize it as an empty plugin function container:
var plugins=[];
module.exports = plugins;
Write your functions that extend the API
To extend an API endpoint, you must populate the plugins
array containing all functions extending the API.
extend.js structure
var plugins=[];
module.exports = plugins;
plugins=[]
is an array of objects , which are functions that constitutes a plugin. Each function is defined as:
{
"resource":"/resourceToExtend",
"method":"GET",
"mode":"override",
"params":[query],
"extender": function(req,content,cType,callback){
callback(null,{"Content extension"});
}
}
- URI: URI of the resource to protect
- method: HTTP method used to call the resource to protect
- authToken : An array of Strings containing the token types authorized by this role
where:
- resource: URI of the resource to extend
- method : HTTP method of the resource to extend
- mode : Mode defining how the resource must be extended. There are four
different possible modes:
override
,before
,after
,before_after
. In detail:- override : overrides the original endpoint definition
- before : the plugin function is executed before the original endpoint
- after : the plugin function is executed after the original endpoint
- before_after: the plugin function is executed both before and after the original endpoint
- params : Array of the fields of Express
req
object that must be passed to the plugin function inreqParams
param - enabled : Optional boolean parameter indicating if the plugin is enabled or not. Default is disabled
- extender: Plugin function definition. This function is invoked end executed by apiextender, defined as:
function(reqParams,content,contentType,callback)
- reqParams : Object containing the Express request params defined in
params
field. E.g. ifparams=["query"]
,reqParams
contains the object{query:req.query}
- content : Response of the original endpoint when
mode
param is set toafter
orbefore_after
.
Null when mode is set tooverride
orbefore
- contentType : Content Type of
content
, e.g.application/json
,text/html
...
Null when mode is set tooverride
orbefore
- callback : callback function to apiextender. It must be invoked with params
error
andnewContent
, defined as:callback(error,newContent)
- error : If an error occurs, apiextender stops the request execution and sends a response to the client
with this object, that must have two fields:
- error_code : HTTP status code
- error_message : error message content
- newContent : response sent to the client if mode is
override
,after
orbefore_after
(for the latter, only for what is executed in theafter
part). If mode is set tobefore
orbefore_after
(for the latter, only for what is executed in thebefore
part), it is an object whose keys are appended to Express'req
.
- error : If an error occurs, apiextender stops the request execution and sends a response to the client
with this object, that must have two fields:
- reqParams : Object containing the Express request params defined in
#### Warning:
If mode
is set to before_after
, it is mandatory to declare in extender
param two functions,
both for the before
and after
actions.
So the extender
parameter is not a function but an object containing two keys:
- before: the function definition for
before
actions - after : the function definition for
after
actions
######Example
{
//....
//....
"mode":"before_after"
"extender":{
"before": function(req,content,cType,callback){
// before logic
callback(null,{"Content extension before"});
},
"after": function(req,content,cType,callback){
// after logic
callback(null,{"Content extension after"});
}
}
}
######Example of extend.js plugin definition
var plugins=[
{
"resource":"/resourceToExtend",
"method":"GET",
"mode":"override",
"params":[query],
"extender": function(req,content,cType,callback){
callback(null,{"Content extension"});
}
},
{
"resource":"/OtherResourceToExtend",
"method":"GET",
"mode":"before",
"params":[query],
"extender": function(req,content,cType,callback){
callback(null,{"Content extension"});
}
}
]
Reference
extend(app)
Function that extends the API. The param app
is the application that you want extend.
install(app,extender,save)
Function that extends an API at runtime, without stopping and restarting your application.
app
is the application that you want extendextender
is the plugin function described in section plugin extender structuresave
if true, the plugin is saved in theextend.js
file, being appended in theplugin
array and becoming permanent.
######Example:
var express=require('express');
var apiextender = require('apiextender');
var app=express();
apiextender.extend(app); // now your API is extensible
// Define an endpoint wrapping apiextender install function that lets you to extend API on the fly
// The access to this endpoint should be protectd with token privileges
app.post("/installPlugin",function(req,res,next){
// check for tokens
//.....
// install and run the plugin
apiextender.install(app,req.body.pluginExtender,req.body.save || false);
res.send({"status":"installed"});
});
Examples
Examples: How to set an API extensible by plugins
From your application home directory type:
$ cd /Your_App_Home
$ npm install apiextender // install apiextender
$ mkdir plugin // Create plugin container folder
$ cd plugin // go into plugin folder
$ vi extend.js // Create plugin container file
Insert this content in extend.js
.
var plugins=[];
module.exports = plugins;
Include the apiextender module in app.js
var express=require('express');
var apiextender = require('apiextender');
var app=express();
apiextender.extend(app);
Examples: How to extend an API
Suppose we want to extend this API:
var express=require('express');
var apiextender = require('apiextender');
var app=express();
apiextender.extend(app); // now your API is exensible
app.get('/overrideOriginal', function(req, res){
res.send({"response":"override"});
});
app.get('/beforeOriginal', function(req, res){
var params=req.optionalParams || null;
var response = params!=null ? {"response":"before", "response_before":params} : {"response":"before"}
res.send(response);
});
app.get('/afterOriginal', function(req, res){
res.send({"response":"after"});
});
app.get('/before_afterOriginal', function(req, res){
var params=req.optionalParams || null;
var response = params!=null ? {"response":"before_after", "response_before":params} : {"response":"before_after"}
res.send(response);
});
Override mode
With no plugin function defined, the endpoint response is:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/overrideOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 23
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"override"} // original response because no extension function
$
Now we extend "/overrideOriginal" endpoint with a plugin, writing a plugin function in extend.js
var plugins=[
{
"resource":"/overrideOriginal", // extend overrideOriginal endpoint
"method":"GET", // extend overrideOriginal endpoint in get method
"mode":"override", // endpoint overrideOriginal must be overrided
"params":[query], // express request "req.query" should be throw to extender function
"extender": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "override" so "content,cType" are both null
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
callback(null,{"response":"Hello " + username + " this is a plugin function that override"});
// ^
// |
// No Error
}
}
];
module.exports = plugins;
Restart your application (or invoke apiextender.install
) to apply the plugin function.
If you curl the endpoint, you can notice that the original endpoint is not executed, being overridden by the plugin function:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/overrideOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 60
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"Hello this is a plugin function that ovveride"} // original response "{"response":"before"}"
// overrided by extension function
$
Before mode
With no plugin function defined, the endpoint response is:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/beforeOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 51
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before"} // original response because no extension function
Now we extend "/beforeOriginal" endpoint with a plugin, writing a plugin function in extend.js
var plugins=[
{
"resource":"/beforeOriginal", // extend beforeOriginal endpoint
"method":"GET", // extend beforeOriginal endpoint in get method
"mode":"before", // plugin function must be executed before original endpoint beforeOriginal
"params":[query], // express request "req.query" should be throw to extender function
"extender": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "before" so "content,cType" are both null
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
callback(null,{"optionalParams":"Hello " + username + " this is a plugin function that extend"});
// ^
// |
// No Error
}
}
];
module.exports = plugins;
Restart your application (or invoke apiextender.install
) to apply the plugin function.
If you curl the endpoint, you can notice that the original endpoint is extended by the plugin function:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/beforeOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 90
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before","response_before":"Hello Alex this is a plugin function that extend"} // original and extended response
After mode
With no plugin function defined, the endpoint response is:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/afterOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 20
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"after"} // original response because no extension function
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/afterOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 20
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"after"} // original response because no extension function
Now we extend "/afterOriginal" endpoint with a plugin, writing a plugin function in extend.js
var plugins=[
{
"resource":"/afterOriginal", // extend afterOriginal endpoint
"method":"GET", // extend afterOriginal endpoint in get method
"mode":"after", // plugin function must be executed after original endpoint afterOriginal
"params":[query], // express request "req.query" should be throw to extender function
"extender": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "after" so "content,cType" are both not null
if(cType==="application/json"){ // if content is a Json
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
content.after_response="Hello " + username + " this is a plugin function that extend";
}
callback(null,content);
// ^
// |
// No Error
}
}
];
module.exports = plugins;
Restart your application (or invoke apiextender.install
) to apply the plugin function.
If you curl the endpoint, you can notice that the original endpoint is extended by the plugin function:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/afterOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 82
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"after","after_response":Hello this is a plugin function that extend"} // original response extended
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/afterOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 87
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"after","after_response":Hello Alex this is a plugin function that extend"} // original response extended
Examples: before_after mode
With no plugin function defined, the endpoint response is:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/before_afterOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 27
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before_after"} // original response because no extension function
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/before_afterOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 27
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before_after"} // original response because no extension function
Now we extend "/before_afterOriginal" endpoint with a plugin, writing a plugin function in extend.js
var plugins=[
{
"resource":"/before_afterOriginal", // extend before_afterOriginal endpoint
"method":"GET", // extend before_afterOriginal endpoint in get method
"mode":"before_after", // plugin function must be executed before and after original endpoint before_afterOriginal
"params":[query], // express request "req.query" should be throw to extender function
"extender":{
"before": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "before_after" so in before function "content,cType" are both null
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
callback(null,{"optionalParams":"Hello " + username + " this is a plugin function that extend"});
// ^
// |
// No Error
},
"after": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "before _after" so in after function "content,cType" are both not null
if(cType==="application/json"){ // if content is a Json
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
content.after_response="Hello " + username + " this is a plugin function that extend";
}
callback(null,content);
// ^
// |
// No Error
}
}
];
module.exports = plugins;
Restart your application (or invoke apiextender.install
) to apply the plugin function.
If you curl the endpoint, you can notice that the original endpoint is extended by the plugin function:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/before_afterOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 154
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{
"response":"before_after",
"response_before":"Hello this is a plugin function that extend", // original and extended response
"response_after":"Hello this is a plugin function that extend"
}
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/before_afterOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 164
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{
"response":"before_after",
"response_before":"Hello Alex this is a plugin function that extend", // original and extended response
"response_after":"Hello Alex this is a plugin function that extend"
}
Error throwing
With no plugin function defined, the endpoint response is:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/beforeOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 51
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before"} // original response because no extension function
$ // now with username param (no response changes)
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/beforeOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 51
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before"} // original response because no extension function
Now we extend "/beforeOriginal" endpoint with a plugin, writing a plugin function in extend.js
. Starting from the
example Examples:before mode
, we edit the plugin function to throw an error if no username
field is sent.
var plugins=[
{
"resource":"/beforeOriginal", // extend beforeOriginal endpoint
"method":"GET", // extend beforeOriginal endpoint in get method
"mode":"before", // endpoint beforeOriginal must be overrided
"params":[query], // express request "req.query" should be throw to extender function
"extender": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "override" so "content,cType" are both null
var username=requestParams.username || null; // if username exist is set otherwise use a void string
if(!username)
callback({"error_code":"400", "error_message":"no username field"},null);
else
callback(null,{"response":"Hello " + username + " this is a plugin function that override"});
// ^
// |
// No Error
}
}
];
module.exports = plugins;
Restart your application (or invoke apiextender.install
) to apply the plugin function.
If you curl the endpoint, you can notice that the original endpoint is not executed after the plugin function,
because no username field is sent. This is due to the fact that before extension
function stops the execution
with an error message to the apiextender callback:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/beforeOriginal
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 37
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"error_message":"no username field"} // only error_message from extended function response
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/beforeOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 90
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before","response_before":"Hello Alex this is a plugin function that extend"} // original and extended response
Examples: A Complete Eexample of plugin extension
With no plugin function defined, the endpoint response is:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/overrideOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 23
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"override"} // original response because no extension function
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/beforeOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 21
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before"} // original response because no extension function
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/afterOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 20
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"after"} // original response because no extension function
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/before_afterOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 27
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before_after"} // original response because no extension function
Now we extend "/....Original" endpoint with a plugin, writing a plugin function in extend.js
.
var plugins=[
{
"resource":"/overrideOriginal", // extend overrideOriginal endpoint
"method":"GET", // extend overrideOriginal endpoint in get method
"mode":"override", // endpoint overrideOriginal must be overrided
"params":[query], // express request "req.query" should be throw to extender function
"extender": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "override" so "content,cType" are both null
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
callback(null,{"response":"Hello " + username + " this is a plugin function that override"});
// ^
// |
// No Error
}
},
{
"resource":"/overrideOriginal", // extend overrideOriginal endpoint
"method":"GET", // extend overrideOriginal endpoint in get method
"mode":"before", // endpoint overrideOriginal must be overrided
"params":[query], // express request "req.query" should be throw to extender function
"extender": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "override" so "content,cType" are both null
var username=requestParams.username || null; // if username exist is set otherwise use a void string
if(!username)
callback({"error_code":"400", "error_message":"no username field"},null);
else
callback(null,{"response":"Hello " + username + " this is a plugin function that override"});
// ^
// |
// No Error
}
},
{
"resource":"/afterOriginal", // extend afterOriginal endpoint
"method":"GET", // extend afterOriginal endpoint in get method
"mode":"after", // plugin function must be executed after original endpoint afterOriginal
"params":[query], // express request "req.query" should be throw to extender function
"extender": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "after" so "content,cType" are both not null
if(cType==="application/json"){ // if content is a Json
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
content.after_response="Hello " + username + " this is a plugin function that extend";
}
callback(null,content);
// ^
// |
// No Error
}
},
{
"resource":"/before_afterOriginal", // extend before_afterOriginal endpoint
"method":"GET", // extend before_afterOriginal endpoint in get method
"mode":"before_after", // plugin function must be executed before and after original endpoint before_afterOriginal
"params":[query], // express request "req.query" should be throw to extender function
"extender":{
"before": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "before_after" so in before function "content,cType" are both null
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
callback(null,{"optionalParams":"Hello " + username + " this is a plugin function that extend"});
// ^
// |
// No Error
},
"after": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "before _after" so in after function "content,cType" are both not null
if(cType==="application/json"){ // if content is a Json
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
content.after_response="Hello " + username + " this is a plugin function that extend";
}
callback(null,content);
// ^
// |
// No Error
}
}
];
module.exports = plugins;
Restart your application (or invoke apiextender.install
) to apply the plugin function.
If you curl the endpoint, you can notice that the original endpoint is extended by the plugin function:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/overrideOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 65
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"Hello Alex this is a plugin function that ovveride"} // original response "{"response":"before"}"
// overrided by extension function
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/beforeOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 90
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"before","response_before":"Hello Alex this is a plugin function that extend"} // original and extended response
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/afterOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 88
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"after","after_response":Hello Alex this is a plugin function that extend"} // original response extended
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/before_afterOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 165
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{
"response":"before_after",
"response_before":"Hello Alex this is a plugin function that extend", // original and extended response
"response_after":"Hello Alex this is a plugin function that extend"
}
Extend API on the fly at runtime
To extend an API on the fly as described in reference section install(app,extender,save), we need to define an endpoint that wraps apiextender install function. Add these code lines to you app.js:
var express=require('express');
var apiextender = require('apiextender');
var app=express();
// .....
// Old app.js logic
// .....
// Define an endpoint that wrap apiextender install function that lets you to extend API on the fly
// The access to this endpoint should be protectd with token privileges
app.post("/installPlugin",function(req,res,next){
// check for tokens
//.....
apiextender.install(app,req.body.pluginExtender,req.body.save || false);
res.send({"status":"installed"});
});
With no plugin function defined, the endpoint response is:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/overrideOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 23
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"override"} // original response because no extension function
Now we extend "/overrideOriginal" endpoint with a plugin, installed on the fly without defining it in extend.js
.
To install, just call /installPlugin
in POST:
EXTENDER='{
"pluginExtender":{
"resource":"/overrideOriginal", // extend overrideOriginal endpoint
"method":"GET", // extend overrideOriginal endpoint in get method
"mode":"override", // endpoint overrideOriginal must be overrided
"params":[query], // express request "req.query" should be throw to extender function
"extender": function(requestParams,content,cType,callback){ // this is the extender Function definition
// the mode is set to "override" so "content,cType" are both null
var username=requestParams.username || ""; // if username exist is set otherwise use a void string
callback(null,{"response":"Hello " + username + " this is a plugin function that override"});
// ^
// |
// No Error
}
},
"save":false; // not save extenson plugin
}'
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X POST http://hostname/overrideOriginal?username=Alex
-d $EXTENDER
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 22
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"status":"installed"} // plugin extension installed
Restart your application (or invoke apiextender.install
) to apply the plugin function.
Without restarting your application, if you curl the endpoint, you can notice that the original endpoint is extended by the plugin function:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/overrideOriginal?username=Alex
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 65
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"response":"Hello Alex this is a plugin function that ovveride"} // original response "{"response":"before"}"
// overrided by extension function
$
License - "MIT License"
MIT License
Copyright (c) 2016 aromanino
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Author
CRS4 Microservice Core Team ([email protected])
Contributors
Alessandro Romanino ([email protected]) Guido Porruvecchio ([email protected])