When using Firebase Cloud Functions, environment configuration for functions code is formed by a set of hierarchical key-value pairs. Values are set using firebase functions:config:set key=value
(part of the Firebase CLI). Dots in the key names make up the hierarchy. All values may be dumped into a JSON file reflecting that hierarchy using firebase functions:config:get
. Values may only be strings. In the functions code itself, values are accessed through functions.config()
(from the Firebase SDK for Cloud Functions), which returns the same data structure as firebase functions:config:get
.
The SDK does not provide any way for the code to express its requirements in terms of configuration. This means functions may be deployed without their required configuration being set on the target project, resulting in a runtime error. Let’s see how the configuration can be validated before deploying to prevent such mistakes.
Since the Firebase CLI can print out the configuration as JSON, it is possible to validate that JSON against a JSON schema. Let’s imagine a function that needs to connect to a Auth0 tenant.
new AuthenticationClient({
domain: functions.config().auth0.domain,
clientId: functions.config().auth0.client_id
});
This functions requires a Auth0 domain and a client ID for that.
{
"auth0": {
"domain": "mydomain.auth0.com",
"client_id": "fake_client_id"
}
}
The presence of those two values can be ensured using the following schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"auth0": {
"type": "object",
"properties": {
"domain": {
"type": "string"
},
"client_id": {
"type": "string"
}
},
"required": [
"domain",
"client_id"
]
}
},
"required": [
"auth0"
]
}
Once this schema is stored in version control next to function code, then the CI pipeline can check the configuration against the schema and fail if it doesn’t match. In the following snippet I use the ajv
npm package to do the validation.
firebase --project=$FIREBASE_PROJECT --token=$FIREBASE_TOKEN functions:config:get > config.json
ajv validate -s config-schema.json -d config.json
The schema can be made stricter by forbidding extra properties in objects (set additionalProperties
to false
).
"true"
and "false"
can be used.I can’t see any solution for the second limitation, however the first one can be solved using TypeScript.
If the functions code is written with TypeScript, then types can be used to describe the configuration data structure.
export interface Auth0Config {
domain: string;
client_id: string;
}
export interface Config {
auth0: Auth0Config;
}
The TypeScript compiler guarantees that the code matches this definition. It’s now a matter of ensuring the configuration matches that definition too. In order to do that, this type definition can be converted into a JSON schema, and then we’re back to our first approach! I use typescript-json-schema to handle the conversion.
typescript-json-schema --required --out config-schema.json functions/src/config.ts Config
The output is equivalent to the schema shown earlier. Use --noExtraProps
to forbid extra properties in the configuration.
Have a look at this issue.