Skip to content

Develop a service from your application

What is it for?

Applications may require server-side code execution that could not, or should not, be in the client. This might be useful for heavy computations or for tasks triggered after some events, typically after data is retrieved through konnectors or mobile/desktop sync, without the user being on the application.

In contrast to konnectors, services have the same permissions as the web application and are not intended to collect information from the outside. It is rather meant to asynchronously analyse data inside the cozy and emit some output once the task is done. However, they share the same mechanisms as the konnectors to describe how and when they should be executed: via our trigger system.

Example

You can find an example of an existing service in the cozy-banks app.

CozyClient instantiation

By using fromEnv, you will be able to use the service in dev mode (via cozy-konnector-dev see Execution) or in production.

const client = CozyClient.fromEnv(process.env, { schema })

You may need to add this line to your service to use the fetch for node:

global.fetch = require('node-fetch').default

Service declaration

The service must be declared in the app manifest. For example:

"services": {
  "onOperationCreate": {
    "type": "node",
    "file": "onOperationCreate.js",
    "trigger": "@event io.cozy.bank.operations:CREATED",
    "debounce": "3m"
  }
}

Here is an explanation of the fields:

  • type: describe the code type (only node for now).
  • file: the single (packaged) file containing the code to execute as a service. It must be the relative path to the built service’s file, not the source one. Expl: /services/name-of-service/built-file.js. Look at your build folder or build/watch console output to find it.
  • trigger: what triggers the service. It must follow the available triggers described in the jobs documentation. In this example, the trigger is a bank operation creation.
  • debounce (optionnal): The debounce parameter can be used to limit the number of jobs created in a burst. It delays the creation of the job on the first input by the given time argument, and if the trigger has its condition matched again during this period, it won’t create another job. Its syntax is the one understood by go’s time.ParseDuration. If this parameter is omitted, the service will be executed as soon as it can.

Build

The service must be packaged into a single file containing all the dependencies. An example of a webpack rule is available here. Note that target: 'node' is important as the service is run as a Node.js process.

In this example, the services are built alongside your app using yarn watch.

⚠️ If you use cozy-script, you should use watch and not start to have the service built.

Stack

As the service is run on a dedicated process on the server side, a running stack is necessary. You can either use a stack installed with docker or directly from source.

Some configuration is required to execute the service and store the produced logs, to facilitate the development. The following instructions are for a stack installed from source, but you can adapt it for a docker installation: you have to download the default config file, modify it as described below and indicate its location through the docker command, as explained here.

In the following, we assume that your $HOME is /home/alice, so change accordingly to your own $HOME.

Copy the default config file if not already done

Create a directory ~/.cozy and copy the default configuration file into it. Be careful, the file name and location matter, as explained in the config documentation.

cp cozy-stack/cozy.example.yaml ~/.cozy/cozy.yaml`

Edit the config file

Edit the file ~/.cozy/cozy.yaml and change the line after the konnectors: entry to have this:

cmd: /home/alice/.cozy/scripts/konnector-node-run.sh

[Optional] Then, after the entry fs:

url: file:///home/alice/.cozy/storage

Create the script to execute the service

Copy the file cozy-stack/scripts/konnector-node-run.sh to ~/.cozy/konnector-node-run.sh:

Then you need to chmod +x ~/.cozy/scripts/konnector-node-run.sh

Be sure to have node in your /usr/bin or /usr/local/bin folder. If not, you can add a symlink to node in one of those folder, for example by typing ln -s $(which node) /usr/local/bin/node

Get your service logs in a isolated file

Edit your ~/.cozy/konnector-node-run.sh by adding a tee output.

set -o pipefail
node "${arg}" 2>&1 | tee -a ~/.cozy/services.log

Now you can tail -f ~/.cozy/services.log to watch logs in real time.

Install your app

To install the app containing the service on your local stack, you must give the path of your build:

cozy-stack apps install <app_name> file://<build_path>
# Example:
# cozy-stack apps install banks file:///home/alice/cozy-banks/build

Each time you make modifications to your service, you must update the app on the stack to propagate the changes:

cozy-stack apps update <app_name>

Env variables

When executing a service, cozy-stack injects some environment variables, listed here, so you can use them in your service, typically through process.env.<env_var_name>.

Execution

The service will be run each time the trigger condition is met, e.g. a bank operation. However, you can force its execution thanks to the cozy-konnector-dev CLI, which can be useful for developement. Be aware, that in that case, you can’t rely on any of the stack provided variables

To install locally:

yarn add --dev cozy-jobs-cli

To run:

yarn run cozy-konnector-dev -m <app_manifest> <mybuiltservice.js>

Be carefull the mybuiltservice.js must be the built file of your service, not the source.

If your mybuiltservice.js is not executable and not recognize by node, you may need this script in your app folder to fix that. Just change the target path.

#!/bin/bash

target="build/services/sync-index-displayName/contacts.js"
firstChar=$(head -c 1 $target)

chmod +x $target
if [[ $firstChar != \#* ]]; then
  sed -i '' '1i\
  #!/usr/bin/env node
  ' $target
fi
cozy-konnector-dev -m manifest.webapp $target