MongoDB stores documents — flexible, JSON-like records — instead of rows in fixed tables. It's one of the three managed database types on Falix, and a natural fit when your data doesn't want a rigid schema. This guide connects Node to it with the official mongodb driver and runs the full set of create-read-update-delete operations.
| At a glance | |
|---|---|
| You need | a MongoDB database (managed or your own server) and a Node.js app server |
| Plan | any |
| Time | about twenty-five minutes |
This goes deeper than Connect your app to a database, which introduced the connection. New to how databases fit a bot? See Designing tables for your bot — the shapes apply to collections too.
Setup: the connection string (mind the authSource)
The panel gives you a MongoDB string shaped like this:
mongodb://user:pass@host:port/dbname?authSource=dbname
Copy it from the Databases page into your .env as DATABASE_URL (why not in code), and install the driver from the Packages page (search mongodb, install, restart).
🎯 Good to know: The
?authSource=dbnameon the end is not optional — it tells MongoDB which database holds your user account. On a managed Falix database that's your own database, soauthSourcematches the database name. Drop it and you'll get an authentication error even with the right password. Keep the string exactly as the panel gives it.
Connect once, reuse everywhere
The mongodb client pools connections internally, so you create it once and share it — never open one per request:
const { MongoClient } = require('mongodb');
const client = new MongoClient(process.env.DATABASE_URL);
async function main() {
await client.connect();
const db = client.db(); // the database from the connection string
const users = db.collection('users'); // a collection is like a table
// ... operations go here ...
await client.close();
}
main().catch(console.error);
client.db() with no argument uses the database named in your connection string. A collection is Mongo's equivalent of a table — but you don't create it or define columns first; it springs into existence the moment you insert a document.
The full CRUD
Everything below was verified on Falix's Node image against MongoDB 7. Drop it into the main() above:
// CREATE — insert one document
await users.insertOne({ _id: 'alice', coins: 100, roles: ['member'] });
// READ — find one by its id
console.log(await users.findOne({ _id: 'alice' }));
// { _id: 'alice', coins: 100, roles: [ 'member' ] }
// UPDATE — $inc adds to a number; upsert creates the doc if missing
await users.updateOne({ _id: 'alice' }, { $inc: { coins: 25 } });
await users.updateOne({ _id: 'bob' }, { $inc: { coins: 10 } }, { upsert: true });
// READ MANY — filter, sort, collect
console.log(await users.find({}).sort({ coins: -1 }).toArray());
// [ { _id: 'alice', coins: 125, ... }, { _id: 'bob', coins: 10 } ]
// DELETE — remove one
await users.deleteOne({ _id: 'bob' });
console.log(await users.countDocuments()); // 1
The parts worth knowing:
_idis every document's unique key. Set it yourself (a Discord user ID makes a perfect_id) or let Mongo generate one. It's automatically indexed, so lookups by_idare fast.- A document is just an object — nested arrays and objects (
roles: ['member']) are stored as-is, no separate table needed. That flexibility is Mongo's whole appeal. - Update operators like
$inc(add to a number),$set(change a field), and$push(append to an array) change a document in place.$incis atomic, which is what you want for a balance or a counter. upsert: trueturns an update into "update if it exists, otherwise create it" — the same first-seen-user convenience as a SQL upsert.find()returns a cursor, not the data. Chain.sort(),.limit(), and finish with.toArray()to get the documents.
No schema, but still design your shape
Mongo won't enforce a schema, but that doesn't mean skipping the thinking. Decide up front what each document holds and keep it consistent — a users collection where some docs call it coins and others balance is a bug waiting to happen. The table-design shapes — one document per user, per guild, a separate collection for lists — apply here just as they do in SQL.
Verify it works
Start the server and watch the Console. The snippet prints the document it inserted, then the sorted list after the updates, then a final count of 1. Seeing coins: 125 on alice after the $inc — the round trip from code to Mongo and back — is your success signal. Restart the app and query again: the documents are still there, because a managed database outlives your server.
Troubleshooting
Authentication failed— almost always the missing or wrongauthSource. Use the panel's string verbatim, including?authSource=dbname. If you rotated the password, copy the fresh string into.env.ECONNREFUSED/ server selection timed out — wrong host or port, or the database is unreachable. Use the exact host from the database page; for your own Mongo server, use its public address and check it's running.Cannot find module 'mongodb'— install it on the Packages page and restart.E11000 duplicate key error— you inserted a document whose_idalready exists. UseupdateOnewithupsert: trueinstead of a secondinsertOne.
Beyond connecting, this is all standard driver territory — the official MongoDB Node.js driver docs cover aggregation, transactions, and indexes.