Running Jazz on the server

Jazz is a distributed database that can be used on both clients or servers without any distinction.

You can use servers to:

  • perform operations that can't be done on the client (e.g. sending emails, making HTTP requests, etc.)
  • validate actions that require a central authority (e.g. a payment gateway, booking a hotel, etc.)

We call the code that runs on the server a "Server Worker".

The main difference to keep in mind when working with Jazz compared to traditional systems is that server code doesn't have any special or privileged access to the user data. You need to be explicit about what you want to share with the server.

This means that your server workers will have their own accounts, and they need to be explicitly given access to the CoValues they need to work on.

Generating credentials

Server Workers typically have static credentials, consisting of a public Account ID and a private Account Secret.

To generate new credentials for a Server Worker, you can run:

npx jazz-run account create --name "My Server Worker"

The name will be put in the public profile of the Server Worker's Account, which can be helpful when inspecting metadata of CoValue edits that the Server Worker has done.

Note: By default the account will be stored in Jazz Cloud. You can use the --peer flag to store the account on a different sync server.

Running a server worker

You can use startWorker to run a Server Worker. Similarly to setting up a client-side Jazz context, it:

  • takes a custom AccountSchema if you have one (for example, because the worker needs to store information in its private account root)
  • takes a URL for a sync & storage server

The migration defined in the AccountSchema will be executed every time the worker starts, the same way as it would be for a client-side Jazz context.

import { 
function startWorker<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | CoreAccountSchema>(options: WorkerOptions<...>): Promise<{
    worker: InstanceOfSchema<S>;
    experimental: {
        inbox: {
            subscribe: Inbox["subscribe"];
        };
    };
    waitForConnection(): Promise<void>;
    subscribeToConnectionChange(listener: (connected: boolean) => void): () => void;
    done: () => Promise<void>;
}>
@categoryContext Creation
startWorker
} from 'jazz-tools/worker';
const {
const worker: {
    profile: {
        name: string;
        inbox?: string | undefined;
        inboxInvite?: string | undefined;
    } & CoMap;
    root: {
        [x: string]: any;
    } & CoMap;
} & Account
worker
} = await
startWorker<co.Account<BaseAccountShape>>(options: WorkerOptions<co.Account<BaseAccountShape>>): Promise<{
    worker: {
        profile: {
            ...;
        } & CoMap;
        root: {
            ...;
        } & CoMap;
    } & Account;
    experimental: {
        inbox: {
            subscribe: Inbox["subscribe"];
        };
    };
    waitForConnection(): Promise<void>;
    subscribeToConnectionChange(listener: (connected: boolean) => void): () => void;
    done: () => Promise<void>;
}>
@categoryContext Creation
startWorker
({
AccountSchema?: co.Account<BaseAccountShape> | undefinedAccountSchema: const MyWorkerAccount: co.Account<BaseAccountShape>MyWorkerAccount, syncServer?: string | undefinedsyncServer: 'wss://cloud.jazz.tools/?key=you@example.com', accountID?: string | undefinedaccountID: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv
The `process.env` property returns an object containing the user environment. See [`environ(7)`](http://man7.org/linux/man-pages/man7/environ.7.html). An example of this object looks like: ```js { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } ``` It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other `Worker` threads. In other words, the following example would not work: ```bash node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo ``` While the following will: ```js import { env } from 'node:process'; env.foo = 'bar'; console.log(env.foo); ``` Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. ```js import { env } from 'node:process'; env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ``` Use `delete` to delete a property from `process.env`. ```js import { env } from 'node:process'; env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ``` On Windows operating systems, environment variables are case-insensitive. ```js import { env } from 'node:process'; env.TEST = 1; console.log(env.test); // => 1 ``` Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
@sincev0.1.27
env
.string | undefinedJAZZ_WORKER_ACCOUNT,
accountSecret?: string | undefinedaccountSecret: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv
The `process.env` property returns an object containing the user environment. See [`environ(7)`](http://man7.org/linux/man-pages/man7/environ.7.html). An example of this object looks like: ```js { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } ``` It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other `Worker` threads. In other words, the following example would not work: ```bash node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo ``` While the following will: ```js import { env } from 'node:process'; env.foo = 'bar'; console.log(env.foo); ``` Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. ```js import { env } from 'node:process'; env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ``` Use `delete` to delete a property from `process.env`. ```js import { env } from 'node:process'; env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ``` On Windows operating systems, environment variables are case-insensitive. ```js import { env } from 'node:process'; env.TEST = 1; console.log(env.test); // => 1 ``` Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
@sincev0.1.27
env
.string | undefinedJAZZ_WORKER_SECRET,
});

worker is an instance of the Account schema provided, and acts like me (as returned by useAccount on the client).

It will implicitly become the current account, and you can avoid mentioning it in most cases.

For this reason we also recommend running a single worker instance per server, because it makes your code much more predictable.

Storing & providing credentials

Server Worker credentials are typically stored and provided as environment variables.

Take extra care with the Account Secret — handle it like any other secret environment variable such as a DB password.