Linking services together
- collections or APIs for managing shared data (e.g. application users or session data)
- common that requires some configuration that would be identical for multiple services
- that provide the same API for different services
For scenarios like these, Foxx provides a way to link services together and allow them to export JS APIs other services can use. In Foxx these JS APIs are called dependencies, the services implementing them are called providers, the services using them are called consumers.
This chapter is about Foxx dependencies as described above. In JavaScript the term dependencies can also refer to bundled node modules, which are an unrelated concept.
Foxx dependencies can be declared in the using the and dependencies
fields:
provides
lists the dependencies a given service provides, i.e. which APIs it claims to be compatible withdependencies
lists the dependencies a given service consumes, i.e. which APIs its dependencies need to be compatible with
A dependency name should be an alphanumeric identifier, optionally using a namespace prefix (i.e. dependency-name
or @namespace/dependency-name
). For example, services maintained by the ArangoDB Foxx team typically use the @foxx
namespace whereas the @arangodb
namespace is reserved for internal use.
There is no official registry for dependency names but we recommend ensuring the dependency names you use are unambiguous and meaningful to other developers using your services.
A provides
definition maps each provided dependency’s name to the provided version:
A dependencies
definition maps the local alias of each consumed dependency against a short definition that includes the name and version range:
"dependencies": {
"myAuth": {
"name": "@example/auth",
"description": "This description is entirely optional.",
"required": false,
"multiple": false
}
}
The local alias should be a valid JavaScript identifier (e.g. a valid variable name). When a dependency has been assigned, its JS API will be exposed in a corresponding property of the service context, e.g. module.context.dependencies.myAuth
.
The value for each dependency should be the database-relative mount path of the service (including the leading slash). Both services need to be mounted in the same database. The same service can be used to provide a dependency for multiple services.
Also as with configuration, a service that declares required dependencies which have not been assigned will not be mounted by Foxx until all required dependencies have been assigned. Instead any attempt to access the service’s HTTP API will result in an error code.
In order to provide a JS API other services can consume as a dependency, the service’s main file needs to export something other services can use. You can do this by assigning a value to the module.exports
or properties of the exports
object as with any other module export:
This also includes collections. In the following example, the collection exported by the provider will use the provider’s rather than the consumer’s, allowing both services to share the same collection:
module.exports = module.context.collection("shared_documents");
Let’s imagine we have a service managing our application’s users. Rather than allowing any consuming service to access the collection directly, we can provide a number of methods to manipulate it:
const auth = require("./util/auth");
const users = module.context.collection("users");
module.exports = (BadCredentialsError = Error) => {
return {
const user = users.firstExample({ username });
if (!user) throw new BadCredentialsError("Wrong username");
const valid = auth.verify(user.authData, password);
if (!valid) throw new BadCredentialsError("Wrong password");
return user;
},
setPassword(user, password) {
const authData = auth.create(password);
users.update(user, { authData });
return user;
}
};
Example usage (the consumer uses the local alias usersApi
):