MongoDB from Python with PyMongo

Connect Python to MongoDB with PyMongo — the panel's authSource connection string, one reusable client, and the full CRUD: insert_one, find, update_one with $inc, and delete_one.

MongoDB stores flexible, JSON-like documents instead of fixed rows — one of the three managed database types on Falix. From Python you reach it with PyMongo, the official driver. This guide connects to a database and runs the full set of create-read-update-delete operations, with the one connection-string detail that trips people up.

At a glance
You need a MongoDB database (managed or your own server) and a Python app server
Plan any
Time about twenty-five minutes

This is the Python companion to MongoDB from Node — same database, same operations. For the bigger picture of connecting any language, see Connect your app to a database.

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). Install the driver by adding pymongo to your requirements.txt — it installs automatically on the next start — or from the Packages page. On Falix the package is named pymongo.

🎯 Good to know: The ?authSource=dbname on the end is required — it tells MongoDB which database holds your user account. On a managed Falix database that's your own database, so authSource matches the database name. Leave it off and you'll hit an authentication error even with the correct password. Use the panel's string exactly as given.

Connect once, reuse everywhere

MongoClient pools connections internally, so create it once when your app starts and reuse it — never one per request:

import os
from pymongo import MongoClient

client = MongoClient(os.environ["DATABASE_URL"])
db = client.get_default_database()   # the database from the connection string
users = db["users"]                  # a collection is like a table

get_default_database() uses the database named in your connection string, so there's nothing to hard-code. A collection is Mongo's equivalent of a table — you don't define it or its columns first; it appears the moment you insert a document.

The full CRUD

Everything below was verified on Falix's Python 3.12 image against MongoDB 7:

# CREATE — insert one document
users.insert_one({"_id": "alice", "coins": 100, "roles": ["member"]})

# READ — find one by its id
print("READ:", users.find_one({"_id": "alice"}), flush=True)
# {'_id': 'alice', 'coins': 100, 'roles': ['member']}

# UPDATE — $inc adds to a number; upsert creates the doc if missing
users.update_one({"_id": "alice"}, {"$inc": {"coins": 25}})
users.update_one({"_id": "bob"}, {"$inc": {"coins": 10}}, upsert=True)

# READ MANY — filter, sort, collect into a list
print("ALL:", list(users.find({}).sort("coins", -1)), flush=True)
# [{'_id': 'alice', 'coins': 125, ...}, {'_id': 'bob', 'coins': 10}]

# DELETE — remove one
users.delete_one({"_id": "bob"})
print("COUNT:", users.count_documents({}), flush=True)   # 1

client.close()

The parts worth knowing:

  • A document is just a dict — nested lists and dicts ("roles": ["member"]) are stored as-is, no separate table required. That flexibility is Mongo's whole appeal.
  • _id is every document's unique, auto-indexed key. Set it yourself (a Discord user ID is a perfect _id) or let Mongo generate one, so lookups by _id are fast.
  • Update operators$inc (add to a number), $set (change a field), $push (append to an array) — change a document in place. $inc is atomic, exactly right for a balance or counter.
  • upsert=True turns an update into "update if it exists, else create it" — the first-seen-user convenience without a separate insert.
  • find() returns a cursor; wrap it in list(...) (or iterate it) to get the documents. Chain .sort(field, -1) for descending order and .limit(n) to cap it.

💡 Tip: flush=True on your print calls makes output show up in the Console immediately — without it, Python may buffer your logs and you'll think nothing happened. This is standard for long-running apps on Falix.

No schema, but still design your shape

PyMongo won't enforce a schema, which is freeing until it isn't — some documents saying coins and others balance is a bug in waiting. Decide each document's shape up front and keep it consistent. The table-design shapes — one document per user, per guild, a separate collection for lists — apply to collections exactly as they do to SQL tables.

Verify it works

Start the server and watch the Console. The snippet prints the inserted document, then the sorted list after the updates (alice at 125 coins), then a final COUNT: 1. That output is the round trip confirmed: code to Mongo and back. Restart the app and query again — the documents persist, because a managed database outlives your server.

Troubleshooting

  • OperationFailure: Authentication failed — nearly always a missing or wrong authSource. Use the panel's string verbatim, including ?authSource=dbname; if you rotated the password, copy the fresh string into .env.
  • ServerSelectionTimeoutError — wrong host/port or an unreachable database. Use the exact host from the database page; for your own Mongo server, use its public address and confirm it's running.
  • ModuleNotFoundError: No module named 'pymongo' — add pymongo to requirements.txt (it installs on start) or install it from the Packages page, then restart.
  • DuplicateKeyError (E11000) — a document with that _id already exists. Use update_one with upsert=True instead of another insert_one.

Past connecting, this is standard driver territory — the official PyMongo documentation covers aggregation, transactions, and indexes.


Next steps

Was this guide helpful?