Backend for the quiz platform "BEACON Q" (Play Store, App Store), based on Node.js and MongoDB.
As of 26 November 2025: ⭐ fully migrated to TypeScript ⭐
Overview:
- ✅ TypeScript
- ✅ Rotating logs
- ✅ MongoDB transactions (requires replica set)
- 🆗 Interactive API documentation (Swagger) - PARTIAL (in progress)
- ✅ Script for automatic schema generation (see
scripts/generateSchemas.js) - ✅ Docker
- MongoDB replica set (3 nodes)
- Automatic MongoDB initialization (keyfile, replica set initialization, user creation; see
docker-compose.ymlandmongo-init.js.example) - Automatic daily MongoDB backup (see
scripts/mongoDBDockerBackup.sh) - Automatic restart for services
- Multi-stage build (see
Dockerfile) - Separate communication network for services
ESLint and Prettier are used for automatic pre-commit linting and formatting of code using the husky and lint-staged packages. (see package.json and .husky/pre-commit)
A Dockerfile and docker-compose.yaml are included to easily get both the Node.js and MongoDB replica set up and running:
- Wipe build and volumes:
docker-compose down --volumes - Run:
docker-compose up --build
However, you are also free to directly run the node app on your local machine (npm run dev), alongside running a MongoDB server on your machine.
This project relies on a number of environment variables and external credentials (Firebase service account JSON). For security reasons, never commit real credentials to the repository — use the provided .env.example as a template and keep your real .env and service account files outside source control.
Note: Node 18+ is required due to the mongoose package (>=9.0.0).
Use the provided .env.example to identify the expected key-values.
What to set (baseline):
PORT— port the server listens on (default:3000).NODE_ENV—developmentorproduction.MONGO_URI— MongoDB connection stringMONGO_INITDB_ROOT_USERNAME,MONGO_INITDB_ROOT_PASSWORD,MONGO_INITDB_DATABASE— used by Docker's mongo initialization.GOOGLE_APPLICATION_CREDENTIALS— path to your Firebase service account JSON file (see below).
- Set up your Firebase project.
- Go to your project's page on Firebase Console.
- Go to the project settings (cog icon top-left) and select the tab "Cloud Messaging".
- Enable "Firebase Cloud Messaging API (V1)".
- Export a service account key file.
- Store the file somewhere in your project, then update the property "GOOGLE_APPLICATION_CREDENTIALS" in your
.envfile to point to it. (indicate the path to it)
Do NOT store this service account key file in your source control, e.g., do not commit it to your git repository.
- Recommended: include the file in your
.gitignore.
The docker-compose.yml config expects two files on the host (root project folder) when running the local Mongo replica set:
mongo-init.js— an initialization script mounted into the primary node (mongo1) at/docker-entrypoint-initdb.d/mongo-init.js.mongo-keyfile— a shared key used by MongoDB for inter-node authentication. (mongo1, mongo2, mongo3)
- Generate the keyfile:
openssl rand -base64 756 > mongo-keyfile(official MongoDB docs) - If you are on a UNIX system (Linux, macOS), you can manually set the required permissions/mode:
chmod 400 mongo-keyfile- If you are on Windows, setting the permissions will not transfer to the UNIX context of Docker.
- To that end,
docker-compose.yamlincludes an initialization service "keyfile-init" to automatically adjust the permissions and owner of the file.
- Create a file
mongo-init.jsin the root of your project, then copy the provided template frommongo-init.js.exampleto this new file and adjust it to your needs.mongo-init.jsis only evaluated by the MongoDB image during the first initialization. Its purpose is to create a non-root database to store the project's data in, with separate authentication credentials and non-admin permissions for security reasons.- NOTE: this is NOT for creating the root (admin) mongo user. That step is done via our inclusion of the fields
MONGO_INITDB_ROOT_USERNAMEandMONGO_INITDB_ROOT_PASSWORDindocker-compose.yaml. Do not use the same username and password for both cases.
Depending on your machine's configuration, the server can fail to connect to external domains, such as Google Firebase.
To fix this, you need to disable ipv6: (example steps for Linux system)
- Append the following lines to
/etc/sysctl.conf:
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.lo.disable_ipv6 = 1
- Save the file.
- Run:
sysctl -p. - Verify if your change was succesful:
cat /proc/sys/net/ipv6/conf/all/disable_ipv6- Output should be "1"!
Internally (docker), the node service can connect to the MongoDB replica set using their assigned container names (mongo1, mongo2, mongo3).
This is facilitated by Docker's own DNS service, which maps each container name to its corresponding IP address in the Docker network.
Subsequently, the host machine does not know anything about these container names, as it cannot resolve the associated IP addresses for them.
Currently, the docker-compose.yml file has been set up to enable mongo connections from both containers and the host machine:
- For each mongo container (mongo1, mongo2, mongo3), we introduce the following key-value mapping under 'environment':
MONGODB_ADVERTISED_HOSTNAME: host.docker.internal - For the primary node (mongo1), we update its
healthcheckcommand to use this new hostname, as it crucially sets up our replica set! (previously, we were simply using the container names) - The
host.docker.internalhostname allows containers (like the Node.js app) to connect to the MongoDB replica set
To connect from your host machine (using mongosh or MongoDB Compass):
First, you need to add host.docker.internal to your hosts file so your machine can resolve it:
- macOS/Linux: Add to
/etc/hosts(requires sudo):echo "127.0.0.1 host.docker.internal" | sudo tee -a /etc/hosts
- Windows: Add to
C:\Windows\System32\drivers\etc\hosts(requires admin):127.0.0.1 host.docker.internal
Then use this connection string:
mongodb://<user>:<password>@host.docker.internal:27017,host.docker.internal:27018,host.docker.internal:27019/?replicaSet=rs0&authSource=admin
Note: Even if you use localhost in the connection string, MongoDB's replica set will redirect you to the host.docker.internal hostnames internally, so the hosts file entry is required.
- Replace
<user>and<password>with the admin credentials we have set up in our.envfile. (MONGO_INITDB_ROOT_USERNAME,MONGO_INITDB_ROOT_PASSWORD)
The manual approach would be to add the IP mappings to our host's DNS resolution setup:
- Windows: edit the file
C:\Windows\System32\drivers\etc\hosts- open the file as admin, e.g., open Notepad as administrator
- Linux/macOS: edit the file
/etc/hosts- use
sudo
- use
- In the given
hostsfile, add a line for each container name, where we map them to the IP address on the host machine that can access our MongoDB server running in the Docker network. For example, in the case of "localhost", add the following entries:
127.0.0.1 mongo1
127.0.0.1 mongo2
127.0.0.1 mongo3
- Finally, to connect to our replica set, use the following connection string (using
mongoshor MongoDB Compass):mongodb://<user>:<password>@mongo1:27017,mongo2:27018,mongo3:27019/?replicaSet=rs0&authSource=admin- replace "user" and "password" with the admin credentials we have set up in our
.envfile. (MONGO_INITDB_ROOT_USERNAME,MONGO_INITDB_ROOT_PASSWORD)
- replace "user" and "password" with the admin credentials we have set up in our
The solution is to manually override your host machine's DNS resolution by editing its local hosts file. This file acts as a local, static DNS record keeper. You are telling your host machine: "When anything tries to look up the name mongo1, use this specific IP address."
- BEACON Q: Encouraging Regular Self-Testing via a Personalized and Gamified Quiz App
- High-Fidelity Simulation Pre-Briefing with Digital Quizzes: Using INACSL Standards for Improving Effectiveness
- Difficulty-Adjusted Quizzes: An Effectiveness Analysis
- Improving Long-Term Retention through Personalized Recall Testing and Immediate Feedback