This is the database backend for bloomlibrary.org, using the parse-server module.
Here is the full Parse Server guide.
-
Make sure you have Node 22 (or 24; see the engines field in package.json).
node --version -
Clone this repo and go into its directory, and install or update the dependencies (use npm, not yarn):
npm install -
Install mongodb server (version 8.0.x, to match the live clusters)
-
Give mongodb a blank directory to work with (create it first if it doesn't exist), and run it:
path/to/mongod.exe --dbpath c:\temp\mongodata -
Start up this server:
npm startOr, to debug, open bloom-parse-server in vscode, F5 (Debug: Launch via NPM). Note that this sets the masterKey to "123", via an environment variable.
-
Setup or update the schema:
curl -X POST -H "X-Parse-Application-Id: myAppId" -H "X-Parse-Master-Key: 123" -d "{}" http://localhost:1337/parse/functions/setupTablesYou should get
{"result":"SetupTables ran to completion."}and see the tables in the dashboard.
But see notes on the
setupTablesfunction incloud/main.jsbefore running on a live database. -
Run Parse Dashboard:
Go to http://localhost:1337/dashboard
You will be required to log in.
For write access:
- username = "master"
- password = the master key ("123")
For read-only access:
- username = "readonly"
- password = the read-only master key ("ro")
See above for setting up the dashboard locally.
Public dashboards:
- Production: https://parsedashboard.bloomlibrary.org
- Development: https://dev-parsedashboard.bloomlibrary.org
You will be required to log in.
For write access:
- username = "master"
- password = the master key
For read-only access:
- username = "readonly"
- password = the read-only master key
curl -X POST \
-H "X-Parse-Application-Id: myAppId" \
-H "Content-Type: application/json" \
-d '{}' \
http://localhost:1337/parse/functions/hello
Notes below on Azure Setup are relevant to deployment, but I wanted to separate out the exact steps a developer would go through to deploy changes.
Deployments are built by GitHub Actions (.github/workflows/deploy.yml): the workflow runs
npm ci + lint on a Windows runner, packages the app with node_modules included, and pushes
the prebuilt zip to the app service. The app service never runs npm, so deploy downtime is only
the zip-extract + iisnode restart, and there is no on-box install to fail or corrupt.
Requirements for the pipeline (one-time setup):
- Repository secrets holding each service's publish profile (portal → app service →
"Download publish profile"):
AZURE_WEBAPP_PUBLISH_PROFILE_UNITTEST,AZURE_WEBAPP_PUBLISH_PROFILE_DEVELOP, andAZURE_WEBAPP_PUBLISH_PROFILE_PRODUCTION_STAGING(that last one is the staging slot's profile, not the production app's). - The old Deployment Center GitHub sync must be disconnected on each service BEFORE its first
pipeline deploy (portal → app service → Deployment Center → Disconnect). While it is
connected, Kudu keeps a "deployment branch" setting that makes every zip deploy fail with
ChangeSetId(develop) does not match ...— and it would double-deploy on each push besides. iisnode.yml(in the repo) pins the node.exe the app runs under; keep it in sync with theWEBSITE_NODE_DEFAULT_VERSIONapp setting when upgrading Node. Notes on this file:- It is deliberately minimal and comment-free: iisnode's parser is line-based (not real YAML), and Kudu's generated format — double quotes, backslashes — is the known-good shape. YAML linters flag the backslashes as invalid escapes; ignore them and don't "fix" the quoting.
loggingEnabled: falsebecause withWEBSITE_RUN_FROM_PACKAGEthe app folder is read-only and iisnode cannot write its stdout/stderr logs there (that failure 500s every request). parse-server's own logs (seePARSE_SERVER_LOGS_FOLDER) are the useful ones and still work.
- App settings on every service (and the production staging slot):
WEBSITE_RUN_FROM_PACKAGE=1— the app runs directly from a read-only mount of the deployed zip. No extraction into wwwroot (extraction is slow and has failed outright on our small instances), and cutover is atomic.PARSE_SERVER_LOGS_FOLDER=C:\home\LogFiles\parse-server— with wwwroot read-only, parse-server can't write its default./logsfolder. (iisnode's own logs are similarly redirected bylogDirectoryin iisnode.yml.)
A one-off deploy of any single service can be run from the GitHub Actions tab ("build-and-deploy" → Run workflow → pick the target).
Changes pushed to the develop branch are deployed automatically by the workflow to
bloom-parse-server-develop (no staging slot; it goes live when the deploy job finishes).
bloom-parse-server-unittest deploys only when triggered manually (GitHub Actions tab →
"build-and-deploy" → Run workflow → target unittest). The two services share slow hardware,
so they deliberately do not deploy together — and unittest updates should be a conscious choice.
To monitor: the workflow run in the GitHub Actions tab. Downtime during a deploy is the extract + restart window, during which the dashboard and the library part of the website are down or stale.
This section applies only to services still deployed by the legacy Deployment Center git sync (production, until its cutover) — the GitHub Actions pipeline never runs npm on the app service.
The legacy build runs npm install on the app service itself, which is slow and can be killed
mid-install (Kudu aborts any build step that produces no output for 60 seconds; the app setting
SCM_COMMAND_IDLE_TIMEOUT = 1800 raises that and should be set on every service).
An interrupted install can leave node_modules corrupted in a way npm install will NOT
self-heal: a package directory whose package.json survives but whose files are gone (a "husk").
Symptom: the app (or the patch-package postinstall during the next deploy) crashes with
Cannot find module '...' or Please verify that the package.json has a valid "main" entry,
even though the package's folder exists, and redeploying doesn't fix it. This happened 2026-07-07
(isarray husk broke every subsequent deploy).
Recovery:
- Kudu console (
https://<app-service>.scm.azurewebsites.net→ Debug console → CMD):cd C:\home\site\wwwrootthenmove node_modules node_modules.broken(renaming is instant; deleting takes ages on these instances). - Deployment Center → Sync, and watch the log. The install truly completed only if you see
the postinstall line
Applying patches... parse-server+<version>.patch ✔. - Verify the site works, then delete
node_modules.brokenwhenever convenient.
Production uses a staging slot with a manual swap. Once changes have been merged to the
master branch, the workflow deploys them to the staging slot; then:
- Go to the Azure portal (portal.azure.com). Access must be granted by LTOps.
- Wait for the "build-and-deploy" workflow run on
masterto finish (GitHub Actions tab). - Open the bloom-parse-server-production app service and click Swap.
- Review settings changes to make sure no app service settings are getting changed accidentally.
- Click Swap.
- The swap takes a couple of minutes; the site stays up (that is the point of the slot).
Once the changes have been deployed, you can run the setupTables function in main.js to modify the schema. See notes there.
We are running three different services:
- bloom-parse-server-unittest
- bloom-parse-server-develop
- bloom-parse-server-production
Each is backed by a single mongodb at mongodb.com. This is how they were made:
-
Create the mongodb on mongodb.com, making sure to select Azure and the same data center. Failing to do this increases response times.
-
In Azure, create a new "Web App" App Service
-
In Azure:App Service:Application Settings:App Settings, create these settings:
-
DATABASE_URI
- mongodb+srv://account:password@something.mongodb.net/database?retryWrites=true&w=majority
-
APP_ID
- you make this up.
-
MASTER_KEY
- you make this up
-
SERVER_URL
-
https://[azure app service name].azurewebsites.net/parse
- Note: Don't leave off that /parse in the SERVER_URL!
-
-
MAILGUN_API_KEY
- mailgun.com "Sending API key" with description "parse-server"
-
EMAIL_BOOK_EVENT_RECIPIENT
- the email address to receive new book notifications
-
EMAIL_REPORT_BOOK_RECIPIENT
- the email address to receive book concern reports from bloomlibrary.org users
-
publicServerURL
- probably obsolete now that we don't use the built-in email feature; currently the same as SERVER_URL
-
PARSE_SERVER_MASTER_KEY_IPS
- "0.0.0.0/0,::/0" to allow master-key use from anywhere.
- Note: it must be
::/0, not::0; parse-server does not recognize::0as allow-all (see UPGRADE-PLAN.md 5.1 — this once broke master-key access entirely).
-
WEBSITE_NODE_DEFAULT_VERSION
- the Node version the app service runs (22.22.2 as of July 2026).
- Not marked as a deployment-slot setting, so it travels with a slot swap.
-
-
Deployments come from the GitHub Actions workflow (see the Deployment section above): download the service's publish profile, add it as the corresponding repository secret, and add the service to
.github/workflows/deploy.yml. Do NOT connect the App Service's Deployment Center to the repository — that legacy path builds on the app service itself and conflicts with the workflow. For production only, the workflow targets a staging slot (bloom-parse-server-production-staging), which is then swapped with the live one; unittest and develop deploy directly with no slot. -
We never touch the schema using the Parse Dashboard or letting queries automagically add classes or fields. Instead, we set up the schema using a Cloud Code function
setupTables. If you haven't set up the database already, follow instructions shown above under "Setup or update the mongodb Schema". Use Azure:App Service:Log stream to monitor progress. Note: During one setup, I found this can be flaky, perhaps because I jumped the gun. So instead I did the curl post forfunctions/testDB, which worked. Then I triedfunctions/setupTablesagain, and this time it worked. -
TODO: Backup, Logging setup.