Scripts and scheduling

In addition to the main entry point which defines your service’sroutes andexports you can define scriptsthat need to be invoked directly and can be used to implement one-off tasksor scheduled and recurring jobs using queues.

These scripts can be declared in the scripts section ofthe service manifest:

  1. "scripts": {
  2. "setup": "scripts/setup.js",
  3. "send-mail": "scripts/send-mail.js"
  4. }

Invoking scripts

Scripts can be invoked manually usingthe web interface,the Foxx CLI orthe Foxx HTTP API.

Additionally the special setup and teardown lifecycle scripts canbe invoked automatically by Foxx as part of a service’s lifecycle (see below).

Script arguments and return values

When invoking a script any arguments will be exposed to the script as theargv array property of the service context.

Any value exported by the script using module.exports will be the script’sreturn value. Please note that this data will be converted to JSON.

Any errors raised by the script will be handled depending on howthe script was invoked:

  • if the script was invoked manually (e.g. using the Foxx CLI), it will returnan error response using the exception’s statusCode property or 500.

  • if the script was invoked from a Foxx job queue, the job’s failure counterwill be incremented and the job will be rescheduled ormarked as failed if no attempts remain.

Examples

The following script will use its argument to generate a personal greeting:

  1. 'use strict';
  2. const { argv } = module.context;
  3. module.exports = `Hello ${argv[0]}!`;

Lifecycle Scripts

Scripts named setup or teardown are considered lifecycle scripts andwill (by default) be invoked automatically by Foxx:

  • when a service is installed, upgraded or replaced, the new service’ssetup script will be executed before it is mounted

  • when a service is removed or replaced, the old service’s teardownscript will be executed before it is unmounted

  • when a service is upgraded, the old service’s teardown script _can_optionally be executed before it is unmounted

However it’s possible to override this behavior as needed.

Note that in these cases the scripts will always be invoked without argumentsand their exports will be ignored.

Setup Script

The setup script is typically used to create collections a service needs,to define indexes or to initialize collections with necessary datalike administrative accounts.

As the setup script may be executed more than once it should be treatedas reentrant: running the setup script again should not result in any errorsor duplicate data:

  1. const { db } = require("@arangodb");
  2. const users = module.context.collectionName("users");
  3. if (!db._collection(users)) {
  4. // This won't be run if the collection exists
  5. const collection = db._createDocumentCollection(users);
  6. collection.ensureIndex({
  7. type: "hash",
  8. unique: true,
  9. fields: ["username"]
  10. });
  11. collection.save({
  12. username: "admin",
  13. password: auth.create("hunter2")
  14. });
  15. }

Teardown Script

The teardown script typically removes the collections and/ordocuments created by the service’s setup script.

In practice teardown scripts are rarely used due to the risk ofcatastrophic data loss when accidentally running the scriptwhile managing the service.

Migrations

Depending on the amount of data managed by the service and the amount of workthat needs to be done to prepare collections for the service,running a setup script on every upgrade can be very expensive.

An alternative approach is to perform incremental steps in separatemigration scripts and run them manually after the service is installed.

A setup script should always create all the collections a service usesbut any additional steps like creating indexes, importing data fixtures ormigrating existing data can safely be performed in separate scripts.

Queues

Services can schedule scripts of any service mounted in the same databaseusing Foxx queues:

  1. "use strict";
  2. const queues = require("@arangodb/foxx/queues");
  3. const queue = queues.get("default");
  4. // later
  5. router.post("/signup", (req, res) => {
  6. const user = performSignup(req.body);
  7. // schedule sending welcome e-mail using a script
  8. queue.push(
  9. {
  10. mount: module.context.mount, // i.e. this current service
  11. name: "send-mail" // script name in the service manifest
  12. },
  13. { to: user.email, body: welcomeEmailText } // arguments
  14. );
  15. });