Getting started with stateful services in JavaScript

Prerequisites

Node version
Cloudstate uses the grpc node package, which compiles a native gRPC implementation using node-gyp. It requires a minimum of node 4.0.
Build tool
Cloudstate doesn’t require any particular build tool, though npm install scripts do need to be run.
protoc
Cloudstate requires using the protoc compiler to serialize your protobuf definitions into the protobuf binary descriptor format. Helper scripts are provided to automatically download the protoc compiler for your platform and do this compilation.
docker
Cloudstate runs in Kubernetes with [Docker], hence you will need Docker to build a container that you can deploy to Kubernetes.

Once you have the above, you need to add the cloudstate package to your project, which can be done by running:

npm install cloudstate --save

Generating the protobuf descriptor set

A Cloudstate user function needs to report to the Cloudstate proxy the gRPC descriptor that its serving, serialized to binary using the Protobuf FileDescriptorSet message type. While there is some support for this in the protobufjs library that is used by Cloudstate to load descriptors, it is somewhat incomplete and buggy. Hence, Cloudstate requires that you precompile this descriptor using protoc instead.

Cloudstate provides a utility that does this for you, downloading the protoc binary for your platform, and running it with the necessary arguments and include paths. This can be run manually by running node_modules/cloudstate/bin/compile-descriptor.js, or we recommend adding it as a prestart script to your npm build:

{
  "scripts": {
    "prestart": "compile-descriptor my-descriptor.proto"
  }
}

Multiple protobuf files can be passed, in addition, any arguments accepted by protoc can be passed, for example, if you are importing files from other directories, you can add those directories as an include path by adding -Ipath/to/protobuf/dir.

By default, the descriptor is written to user-function.desc, if you wish to change this, you can set --descriptor_set_out=my-descriptor.desc. Note that if you output the descriptor to a different path, you will also need to pass that custom path to the constructor of the CloudState class when you got to instantiate it.

package.json example

A minimal package.json for a shopping cart example is shown below:

{
  "name": "shopping-cart",
  "version": "0.1.0",
  "dependencies": {
    "cloudstate": "^0.0.1"
  },
  "scripts": {
    "prestart": "compile-descriptor shoppingcart.proto",
    "start": "node index.js"
  }
}

Protobuf files

You can place protobuf files in your project wherever you like, for example, in the root directory, or in a directory named protos. In the package.json above we’ve placed the shopping cart application example shown earlier in gRPC descriptors in a file in the root folder called shoppingcart.proto.

Creating and starting a server

There are two ways to create and start a Cloudstate gRPC server. The first is to create an Entity, and invoke start on it. This allows creating a server that serves a single entity, with the default options. We’ll look at this more in the subsequent pages. Alternatively, you can use the CloudState class, add one or more entities to it, and then invoke start, like so:

const cloudstate = require("cloudstate");
const shoppingcart = require("./shoppingcart");

const server = new cloudstate.CloudState();
server.addEntity(shoppingcart);
server.start();

If you created your protobuf file descriptor set at a different location to the default of user-function.desc, you can configure that here:

const server = new cloudstate.CloudState({
    descriptorSetPath: "my-descriptor.desc"
});

For the full range of options available on the CloudState class, see CloudState~options.

The source code for this page can be found here.