The Service Node allows you to run your own custom services. A Service Node service is a node.js application that Service Node launches instances of as child processes. Different service instances communicate with each other through a publish/subscribe (pubsub) bus.
This template includes:
- Sample package.json fields for launching service instances with custom parameters
- pubsub messaging with the catch-messaging package
- Sample package.json fields for defining commands the service can receive
- Sample functions to parse commands
- Sample package.json fields for defining responses the service can send
- Sample functions to send responses
- Sample package.json fields for building the service
- Functions to exit cleanly
Service parameter values are entered in Service Node's dashboard and passed to your application when the service instance is started.
This template uses yargs to parse the service parameters.
The following parameter types are support:
serviceName
The service name is always passed to to the service instance as argv.serviceName. serviceName does not need to be included in package.json.string
A string.number
A number.bool
Eithertrue
orfalse
.array
An array of strings. One array element is selectable in Service Node's dashboard.
Here is an example of how the parameters
field should look in package.json:
"parameters": [
{
"parameter": "stringParameter",
"name": "Sample String Parameter",
"type": "string",
"default": "",
"required": true,
"placeholder": "Enter the string.",
"tooltip": "A sample string parameter."
},
{
"parameter": "numberParameter",
"name": "Sample Number Parameter",
"type": "number",
"default": 23,
"required": false,
"placeholder": "Enter the number.",
"tooltip": "A sample number parameter."
},
{
"parameter": "debug",
"name": "Debug",
"type": "bool",
"default": false,
"required": false,
"placeholder": "",
"tooltip": "Prints debug messages when enabled."
},
{
"parameter": "arrayParameter",
"name": "Sample Array Parameter",
"type": "array",
"options": [
{
"label": "Value 1",
"value": "value 1"
},
{
"label": "Value 2",
"value": "value 2"
}
],
"default": "string",
"required": false,
"tooltip": "A sample array parameter."
}
]
Service Node uses (redis)[https://redis.io] pubsub for messaging between services. Values are published to a channel and any service subscribed to that channel is informed whenever the channel is published to. A pubsub message consists of a channel and a value. The value may be empty.
Your service can use the pubsub bus directly with node-redis, or use Catch Messaging. Be careful to not create pubsub loops when using the pubsub bus directly. This will decrease Service Node's performance.
Catch Messaging integrates redis pubsub with a messaging format that works with the Service Node dashboard. To use Catch Messaging, your app must be programmed to accept commands and emit responses as strings.
Default commands and responses are defined in package.json. The messages can be modified per instance in Service Node's dashboard and csv files can be imported through the dashboard to handle large numbers of messages.
Catch Messaging analyzes the pubsub channels when the service starts for each command and response to prevent loops. If a command uses the same channel as one of the responses, the command will not get subscribed to the channel.
Service Commands are sent to your application with repl and parsed to call a function.
Defining your commands in package.json allows them to be edited in Service Node's dashboard and be assigned to a pubsub channel.
In the following example, when another service publishes to example.service.connect
, the string connect
will be sent to the processCommand()
function in this template.
Set useHex
to true to use hexadecimal formatting for the command.
endWith
defines the final charachter(s) of the command. Valid values are:
none
No extra characters.r
Carriage return.n
Line feed.rn
Carriage return and Line feed.
The pubsub value can be passed into the command by including the string #PAYLOAD#
in the command pattern.
Here is an example of how the commands
field should look in package.json:
"commands": [
{
"name": "Connect",
"editable": true,
"pattern": "connect",
"useHex": false,
"endWith": "none",
"descriptions": [
{
"title": "Description",
"body": "Connects the service."
}
],
"channel": "example.service.connect"
},
{
"name": "Disconnect",
"editable": true,
"pattern": "disconnect",
"useHex": false,
"endWith": "none",
"descriptions": [
{
"title": "Description",
"body": "Disconnects the service."
}
],
"channel": "example.service.disconnect"
},
{
"name": "Send Command",
"editable": true,
"pattern": "#PAYLOAD#",
"useHex": false,
"endWith": "none",
"descriptions": [
{
"title": "Description",
"body": "Sends the channel value to the service."
}
],
"channel": "example.service.send"
}
]
Service Responses are sent from your application to the Catch Messaging package and published to the channel defined.
Defining your responses in package.json allows them to be edited in Service Node's dashboard and be assigned to a pubsub channel.
In the following example, when your app sends the string connected
to Catch Messaging, a value of null
is published to the channel example.service.connected
.
Setting the persist
field to true tells Service Node to store published messages to the even timeline and the store the channel/value pair in its own key.
useHex
and endWith
fields are used the same way as Service Commands.
useRegularExpression
allows you to embed a value to be published in the response string. Include ^(.+)$
in the response pattern to embed a value. Everything not included in the pattern before the (
will be published as the value.
If a response pattern is defined as:
^lighting load = (.+)$
And the service sends to Catch Messaging:
lighting load = 77
The value 77
gets published the channel defined for the response.
Here is an example of how the responses
field should look in package.json:
"responses": [
{
"name": "Connected",
"editable": true,
"pattern": "connected",
"useHex": false,
"endWith": "none",
"descriptions": [
{
"title": "Description",
"body": "Indicates when the service is connected."
}
],
"channel": "example.service.connected",
"persist": false
},
{
"name": "Disconnected",
"editable": true,
"pattern": "disconnected",
"useHex": false,
"endWith": "none",
"descriptions": [
{
"title": "Description",
"body": "Indicates when the service is disconnected."
}
],
"channel": "example.service.disconnected",
"persist": false
},
{
"name": "Received",
"editable": true,
"pattern": "^(.+)$",
"useHex": false,
"endWith": "none",
"useRegularExpression": true,
"descriptions": [
{
"title": "Description",
"body": "Returns all replies without special ending characters from the service."
}
],
"channel": "example.service.received",
"persist": false
}
]
After your service is uploaded to Service Node, it is built into a single file using pkg.
The entry point of you application must be included in the bin
field of package.json:
"bin": "main.js"
Define any external assets your build requires in package.json as follows (this example includes all .js
files from a directory named js
):
"pkg": {
"scripts": "js/**/*.js"
}
See the pkg repository for more information on including external assets.
It is a good idea to create a test build before uploading to Service Node to see if there are any issues. See the pkg repository for details.
To prepare your application for Service Node, compress all the needed files into a .zip
file. Service Node runs npm install during the build process, so there is no need to include the node_modules
folder. Any unneeded files should be excluded to keep the compressed file size down for uploading.
It is a good idea to include a package-lock.json
file in your zip upload. Buidling on Service Node may fail without it. Run npm install
locally to create a package-lock.json
.
Then click the Gear icon on you Service Node dashboard, navigate to Service Library
and click Upload New Service
.
Service Node then does the following:
- Uploads the zip file.
- Unzips it.
- Runs npm install.
- Runs pkg.
- Saves package.json and the file compiled with pkg to create future service instances.
- Deletes all other files.
To create an instance of your service, click the Gear icon on you Service Node dashboard, navigate to Services
and click Add New Service
. Select your service from the list, give it a name, adjust the parameters and messages and click Save
. The service will automatically start when saved and every time Service Node starts.