Tutorials
APIs are defined in modules with specific members and functions.
Functions may be accessed via HTTP, WebSocket, or TCP.
On each call paramters are verified and security access is checked.
The following is a very simple example of an API module:
var sh = require(global.C.BASE_DIR + "/lib/shutil.js");
var example = exports;
example.desc = "example api";
example.functions = {
echo: {desc: "one parameter api example", params: {param1: {dtype: "string"}}, security: []}
};
example.echo = function (req, res, cb) {
res.add(sh.event("example.echo", req.body.param1));
return cb(0);
};
There are two classes of APIs - Core and App. The API definitions and capabilities are identical. The purpose of the segmentation is mostly to facilitate code separation for upgrades.
These contain basic functionality such as registration, counters, basic game play, and others. They ship with Shelly and are located in:
They can be viewed in the admin via the "Core APIs" menu: http://localhost:5100/core.html
These are the developer defined APIs and are located in the APP_API_DIR config setting
shelly.start({
APP_API_DIR: __dirname + "/apis",
});
They can be viewed in the admin via the "App APIs" menu: http://localhost:5100/core.html?api=api.app
$ cp ~/node_modules/shellyjs/example/* .
This will create the flowing
./app.js - mail application file
./apis - directory where to place your APIs
./apis/example/example.js - example api to be used as a template
./games - directoy used by the Games Core API
$ node app.js
To allow for parameter, security and easy testing each API function has a description member in the <module>.functions object
Edit the file ~/apis/example/example.js
var sh = require(global.C.BASE_DIR + "/lib/shutil.js");
var example = exports;
example.desc = "example api";
example.functions = {
echo: {desc: "one parameter api example", params: {param1: {dtype: "string"}}, security: []},
hello: {desc: "my first api function", params: {param1: {dtype: "string"}}, security: []}
};
example.echo = function (req, res, cb) {
res.add(sh.event("example.echo", req.body.param1));
return cb(0);
};
In the above code sample line 8 has been added to define the "hello" function in the "example" api
Now just create the function to be called with the fixed signature of function(req, res, cb)
Edit the file ~/apis/example/example.js
var sh = require(global.C.BASE_DIR + "/lib/shutil.js");
var example = exports;
example.desc = "example api";
example.functions = {
echo: {desc: "one parameter api example", params: {param1: {dtype: "string"}}, security: []},
hello: {desc: "my first api function", params: {myName: {dtype: "string"}}, security: []}
};
example.echo = function (req, res, cb) {
res.add(sh.event("example.echo", req.body.param1));
return cb(0);
};
example.hello = function (req, res, cb) {
res.add(sh.event("example.hello", "hello " + req.body.myName + "!"));
return cb(0);
};
In the above code sample lines 16-19 define the new function "hello" in the "example" API
This function is now callable from the HTTP, WebSocket, and TCP interface.
The req and res objects are similar to the express req and res objects, but work for WebSockets and TCP also.
You can add multiple response objects. Moreover you can call res.add() multiple times in a single call. The HTTP interface returns an array objects, while the WebSocket and TCP interface return each individual object.
The sh.event function on line 12 and line 17 is just a convenience function that creates a standard envelope around each response. It creates an object like:
{
"event": "example.echo",
"ts": 1378189438607,
"data": "foo"
}
Each server in a cluster can run multiple workers for the HTTP, WebSocket, TCP, Admin, Game, Mailer, and Matcher processes. These are configured per server in with the CLUSTER_NUM_*settings
global.CDEF("CLUSTER_NUM_SOCKET", 2);
global.CDEF("CLUSTER_NUM_TCP", 1);
global.CDEF("CLUSTER_NUM_REST", 1);
global.CDEF("CLUSTER_NUM_ADMIN", 1);
global.CDEF("CLUSTER_NUM_GAMES", 1);
global.CDEF("CLUSTER_NUM_MATCHER", 1);
global.CDEF("CLUSTER_NUM_MAILER", 1);
In the above example the server will spawn 2 WebSocket workers and one each for the HTTP, TCP, Admin, Games, Matcher, and Mailer worker types.
Servers in a cluster register themselves on startup and can be added and removed dynamically.
To demonstrate clustering in the development environment (sqlite) you can launch a second server on the same machine by creating a different config file so the listening ports do not conflict.
Both servers must have access to he same sqlite database file.
The following example can be found in the ~/node_modules/shelly/examples/config2 directory:
global.CDEF("ADMIN_PORT", 6100);
global.CDEF("REST_PORT", 6101);
global.CDEF("GAMES_PORT", 6102);
global.CDEF("SOCKET_PORT", 6110);
global.CDEF("TCP_PORT", 6111);
global.CDEF("CLUSTER_URL", "tcp://localhost:6151");
// don't run a mail queue processor on this server
global.CDEF("CLUSTER_NUM_MAILERS", 0);
// pick up all the other default configs
require(global.C.BASE_DIR + "/config/main.js");
This assumes you have already copied the examples directory as done in the Create API section
$ node app.js
The default first argument is a valid config directory with a main.js in it.
$ node app.js CONFIG_DIR=./config2
[
{
"event": "cluster.servers",
"ts": 1378193108072,
"data": {
"5c3817f0-d7a2-11e2-964f-d147e3ee063a": {
"clusterUrl": "tcp://localhost:6151",
"socketUrl": "ws://localhost:6110",
"oid": "5c3817f0-d7a2-11e2-964f-d147e3ee063a",
"created": 1378193106281,
"modified": 1378193106282
},
"a81f9560-dac9-11e2-8aa4-7b114762795c": {
"clusterUrl": "tcp://localhost:5151",
"socketUrl": "ws://localhost:5110",
"oid": "a81f9560-dac9-11e2-8aa4-7b114762795c",
"created": 1378109876920,
"modified": 1378109876921
}
}
}
]
To run a cluster on separate machines the Redis data store wrapper must be used by setting the DB_WRAPPER and DB_OPTIONS in the "example/config/main.js". For more details see Use Redis
global.CDEF("DB_WRAPPER", global.C.BASE_DIR + "/lib/db/shredis.js");
global.CDEF("DB_OPTIONS", {port: 6379, host: "127.0.0.1"});
Then just install and start a server on each machine.
$ node app.js
Use the setup for your platform at Redis.io Redis Install
Add the following settings to the application configuration file in example/config/main.js
global.CDEF("DB_WRAPPER", global.C.BASE_DIR + "/lib/db/shredis.js");
global.CDEF("DB_OPTIONS", {port: 6379, host: "127.0.0.1"});
By default Redis installs with no password required. Use the DB_OPTIONS to pass in any Redis specific settings. Shelly uses the Redis client. Details for this client can be found at the Redis client GitHub repo.
If your Redis server has a password required your configuration will look something like:
global.CDEF("DB_WRAPPER", global.C.BASE_DIR + "/lib/db/shredis.js");
global.CDEF("DB_OPTIONS", {port: 6379, host: "127.0.0.1", password:"foo"});