You might find Lauren Schaefer’s series of articles on “DevOps + MongoDB Realm Serverless Functions = ” an interesting read: https://www.mongodb.com/how-to/unit-test-realm-serverless-functions
And her video an interesting watch: DevOps + MongoDB Serverless = Wow! - YouTube
I don’t know of other “official” guidelines on this, but I know that the team is currently revisiting the app export/import and configuration file format, in part to tackle this very problem. I think it would be valuable for them to hear what you think would be a good solution to the problem.
As a (temporary) workaround, I’ll share what I’ve personally done on one of my projects.
For function definitions
I’ve stored the name of my environment specific database in a “value” (named “defaultDatabase”) and retrieve it from functions like so:
context.services
.get("mongodb-atlas")
.db(context.values.get("defaultDatabase"))
But, that only solves the issue for function definitions.
For config files
For the configuration files I’ve written a small (TypeScript) script that takes the declaration of my “staging” app and patch in values that are relevant for production:
scripts/search-replace.ts
import path from "path";
import fs from "fs-extra";
import glob from "glob";
import deepmerge from "deepmerge";
if (process.argv.length < 4) {
throw new Error("Expected at least 4 runtime arguments");
}
const args = process.argv.slice(process.argv.length - 3);
const mappingPath = path.resolve(args[0]);
const appPath = path.resolve(args[1]);
const destinationPath = path.resolve(args[2]);
console.log(`Copying ${appPath} to ${destinationPath} (overwriting)`);
fs.removeSync(destinationPath);
fs.copySync(appPath, destinationPath, { overwrite: true });
const mapping = fs.readJSONSync(mappingPath);
for (const [fileGlob, replacement] of Object.entries(mapping)) {
const files = glob.sync(fileGlob, { cwd: destinationPath });
for (const relativeFilePath of files) {
const filePath = path.resolve(destinationPath, relativeFilePath);
const content = fs.readJSONSync(filePath);
const mergedContent = deepmerge(content, replacement as any);
fs.writeJSONSync(filePath, mergedContent, { spaces: 2 });
}
}
realm-app/production.json
{
"config.json": {
"app_id": "my-app-prod-abcde",
"hosting": {
"custom_domain": "app.my-app.io",
"app_default_domain": "my-app-prod-abcde.mongodbstitch.com"
},
"custom_user_data_config": {
"database_name": "my-app-prod"
}
},
"values/defaultDatabase.json": {
"value": "my-app-prod"
},
"services/mongodb-atlas/rules/*.json": {
"database": "my-app-prod"
}
}
I execute this with ts-node
like so:
ts-node --project scripts/tsconfig.json scripts/search-replace.ts realm-app/production.json realm-app production-realm-app
It basically copies the app config files from realm-app
and for every key in production.json
it finds files matching the glob (specified by the key’s string value) and deep-replace the values defined by the value in those files.
I hope that all makes sense, feel free to use the code above if you choose to go down the same path as me.