Services

APIs are defined via Services. Put simply, a Service is an HTTP server that exposes a JSON REST API and which is defined as a tree of Endpoints.

Services and Endpoints

All Service definitions follow the same general structure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var carbon = require('carbon-io')
var o  = carbon.atom.o(module)
var __ = carbon.fibers.__(module)

__(function() {
  module.exports = o.main({
    _type: carbon.carbond.Service,
    port: 8888,
    endpoints: {
      // Endpoint definitions go here
    }
  })
})

Here is an example of a simple Service that runs on port 8888 and that defines a single Endpoint at the path /hello which a defines a single get operation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
var carbon = require('carbon-io')
var o  = carbon.atom.o(module)
var __ = carbon.fibers.__(module)

__(function() {
  module.exports = o.main({
    _type: carbon.carbond.Service,
    port: 8888,
    endpoints: {
      hello: o({
        _type: carbon.carbond.Endpoint,
        get: function(req) {
          return { msg: "Hello World!" }
        }
      })
    }
  })
})

Service middleware

You can register Express-style middleware for your service via the middleware property on your Service object:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
var carbon = require('carbon-io')
var o  = carbon.atom.o(module)
var __ = carbon.fibers.__(module)

__(function() {
  module.exports = o.main({
    _type: carbon.carbond.Service,
    port: 8888,
    middleware: [
      function(req, res, next) {
        console.log('This is called on every request')
        next()
      }
    ],
    endpoints: {
      // Endpoint definitions go here
    }
  })
})

Running Services from the command line

In most cases you will want to start and stop your Service from the command line.

This can be done by ensuring the value of o you are using to define your Service is the main version of the library, as shown below on line 6:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var carbon = require('carbon-io')
var o  = carbon.atom.o(module)
var __ = carbon.fibers.__(module)

__(function() {
  module.exports = o.main({
    _type: carbon.carbond.Service,
    port: 8888,
    endpoints: {
      // Endpoint definitions go here
    }
  })
})

You can then start your Service like this:

% node <path-to-your-app>/lib/HelloService.js
 [Mon Feb 09 2015 21:56:41 GMT-0800 (PST)] INFO: Service starting...
 [Mon Feb 09 2015 21:56:41 GMT-0800 (PST)] INFO: Service listening on port 8888
 [Mon Feb 09 2015 21:56:41 GMT-0800 (PST)] INFO: Service started

You can use -h or --help to get command help from your Service:

Usage: node <path-to-your-app>/lib/HelloService.js [command] [options]

command     
  start-server        start the api server (default)
  gen-static-docs     generate docs for the api

Options:
   -v VERBOSITY, --verbosity VERBOSITY   verbosity level (trace | debug | info | warn | error | fatal)

Environment variables: 
  <none>

You can see that there are two sub-commands. One for starting the server and another for generating documentation for your Service.

The default sub-command is start-server, and will be run if you omit a sub-command (e.g. $> node <path-to-your-app>/MyService):

Usage: node <path-to-your-app>/lib/HelloService.js start-server [options]

Options:
   -v VERBOSITY, --verbosity VERBOSITY   verbosity level (trace | debug | info | warn | error | fatal)
   -p PORT, --port PORT                  port
   -n HOSTNAME, --hostname HOSTNAME      the hostname to bind  [0.0.0.0]
   -d DB_URI, --dbUri DB_URI             MongoDB connection string
   --cluster                             use node cluster
   --num-cluster-workers NUM             fork NUM cluster nodes (default is to fork a worker for each CPU)  [0]
   --exit-on-cluster-worker-exit         if this flag is set, the master will exit if a work dies, otherwise a warning will be logged
   --swagger                             mount swagger endpoints
   --enable-busy-limiter                 send 503 responses when the node process becomes too "busy"
   --fiber-pool-size SIZE                set the fiber pool size  [120]

start the api server (default)
Environment variables: 
  <none>

Embedding Services into larger applications (advanced use)

While you will usually run your Services via the command line as a top-level application, Service objects can also be used as a library (although it is not common).

By using the start and stop methods, you can manage the Service lifecyle manually.

These methods have both an asynchronous and a synchronous interface:

Asynchronous example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var carbon = require('carbon-io')
var o  = carbon.atom.o(module)

var myService = o({ // IMPORTANT: do not use "o.main" here
  _type: carbon.carbond.Service,
  port: 8888,
  endpoints: {
    hello: o({
      _type: carbon.carbond.Endpoint,
      get: function(req) {
        return { msg: 'Hello World!' }
      }
    })
  }
})

function startService(done) {
  myService.start({}, function(err) {
    if (err) {
      myService.logError('Error starting service ' + err)
      done(err)
    } else {
      myService.logInfo('Service started')
      //
      // Do stuff...
      //
      myService.stop(function(err) {
        myService.logInfo('Service stopped')
        done(err)
      })
    }
  })
}

if (module === require.main) {
  startService(function(err) {
    process.exit(err ? 1 : 0)
  })
}

Synchronous example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var carbon = require('carbon-io')
var __ = carbon.fibers.__(module)
var o = carbon.atom.o(module)

var myService = o({ // IMPORTANT: do not use "o.main" here
  _type: carbon.carbond.Service,
  port: 8888,
  endpoints: {
    hello: o({
      _type: carbon.carbond.Endpoint,
      get: function(req) {
        return { msg: "Hello World!" }
      }
    })
  }
})

function startService() {
  try {
    myService.start()
    myService.logInfo('Service started')
    //
    // Do stuff...
    //
    myService.stop()
    myService.logInfo('Service stopped')
  } catch (e) {
    myService.logError("Error starting service " + err)
    return 1
  }
  return 0
}

if (module === require.main) {
  __(function() {
    process.exit(startService())
  })
}

Important note: you should not find yourself starting and stopping services like this (by manually calling start and stop) frequently. In most use-cases you will simply use the command line invocation described in the previous section.