Controllers #
Kuzzle's API is divided into controllers, each exposing executable actions (see API reference).
Plugins can extend Kuzzle's API by adding new controllers to it.
Security access to plugin controllers must be given (or denied), using the exact same way as with native API controllers.
Querying plugins controllers #
To avoid naming conflicts, Kuzzle prefixes plugin controllers names with the plugin name.
HTTP #
URL: http://<server>:<port>/_plugin/<plugin name>/<url defined by the plugin>/<resources>
Method: <verb defined by the plugin>
Other protocols #
{
"controller": "<plugin name>/<added controller name>",
"action": "<action of the added controller>",
...
}
Creating a Controller Route #
In order to create a new controller, the plugin must expose the following properties:
- A
controllers
object, describing the controller(s) to add. It will automatically be made available to any network protocol, except for HTTP - A
routes
objects, describing how the controller(s) should be exposed to the HTTP protocol - The controller's actions, which are functions taking a
Request
object as an argument. These functions must return a promise, resolved with the action's result, or rejected with a KuzzleError object.
Return a response #
By default, Kuzzle wraps a controller action's result in a Kuzzle Response object.
Consider the following action:
async init (config, context) {
this.controllers = {
myController: {
saySomething: 'saySomething'
}
}
};
async saySomething (request) {
return {
foo: 'bar'
};
}
When this action is successfully called, the following answer will be returned by Kuzzle:
{
"requestId": "32dfbe90-34e1-43c0-a857-25b715b28a1b",
"status": 200,
"error": null,
"controller": "myPlugin/myController",
"action": "saySomething",
"volatile": null,
"result":
{
"foo": "bar"
}
}
However, it is possible to tell Kuzzle not to wrap the returned value in a Kuzzle Response and send it as it is.
To do this, the raw
property of the response object must be set to true
.
For example, to return an arbitrary JSON:
async saySomething (request) {
const json = JSON.stringify({ foo: 'bar' });
request.response.raw = true;
request.response.headers['Content-Type'] = 'application/json';
return json;
}
When this action is successfully called, the following answer will be returned by Kuzzle:
{
"foo": "bar"
}
Query normalization #
Kuzzle normalizes queries into Request objects.
Quick summary of how queries are normalized:
HTTP:
- dynamic arguments provided in the URL, and query string arguments are stored in
request.input.args
- the body content is made available in
request.input.body
- if the URL contains an
index
, acollection
or a_id
argument, it will be stored inrequest.input.resource
- request headers can be found in
request.context.connection.misc.headers
- dynamic arguments provided in the URL, and query string arguments are stored in
Other protocols:
- the
body
property is stored inrequest.input.body
- these root properties are available in
request.input.resource
:index
,collection
,_id
- any other properties at the root of the query object will be stored in
request.input.args
- the
Automatic Events Generation #
Kuzzle triggers events on all controller routes, including those added by plugins.
Read more about these automatic controller events here.
Example #
module.exports = class ControllerPlugin {
constructor() {
/*
Adds a new "newController" controller to Kuzzle's API.
That new controller exposes 2 actions: myAction, and myOtherAction
These actions point to functions exposed to Kuzzle by the plugin.
Network protocols (other than HTTP) will be able to invoke this new
controller with the following JSON object:
{
controller: '<plugin name>/newController',
action: 'myAction',
...
}
*/
this.controllers = {
newController: {
myAction: 'actionFunction',
myOtherAction: 'otherActionFunction'
}
};
/*
To expose the new controller, it must be registered to Kuzzle's HTTP router.
To do so, a "routes" property must be exposed by the plugin, as an array
of objects. Each object is an API route, with the following properties:
- verb: HTTP verb
- url: HTTP address. Any parameter starting with a ':'
will be made dynamic by Kuzzle (its value is stored in request.input.args)
- controller: plugin controller name, as exposed in the "this.controllers" object
- action: controller action to execute
The first route exposes the following GET URL:
http://<kuzzle server>:<port>/_plugin/<plugin name>/foo/<dynamic value>
Kuzzle will call the function 'actionFunction' with a Request object,
containing the "name" property: request.input.args.name = '<dynamic value>'
The second route exposes the following POST URL:
http://<kuzzle server>:<port>/_plugin/<plugin name>/bar
Kuzzle will provide the content body of the request in the Request object
passed to the function 'otherActionFunction', in the request.input.body
property
*/
this.routes = [
{
verb: 'get',
url: '/foo/:name',
controller: 'newController',
action: 'myAction'
},
{
verb: 'post',
url: '/bar',
controller: 'newController',
action: 'myOtherAction'
}
];
}
/*
Required plugin initialization function
(see the "Plugin prerequisites" section)
*/
init(customConfig, context) {
// plugin initialization
}
/*
Implements the action newController/myAction
Takes a Request object as an argument, and MUST return
a promise resolved (or rejected) with the action's result
This result can be of any JS type (scalar, object, array), and
will be used to build a response to send to the requesting client
*/
actionFunction(request) {
// do action
// optional: set network specific headers
if (request.context.protocol === 'http') {
// expires in 1h
request.response.setHeader(
'expires',
new Date(Date.now() + 3600000).toUTCString()
);
}
// Resolve with the result content. For instance:
return Promise.resolve({ acknowledge: true });
}
/*
Implements the action newController/myOtherAction
Takes a Request object as an argument, and MUST return
a promise resolved (or rejected) with the action's result
This result can be of any JS type (scalar, object, array), and
will be used to build a response to send to the requesting client
*/
otherActionFunction(request) {
// do action
return Promise.resolve(/* result content *);
}
};