Integrating FastAPI and MongoDB

Ethan Cerami
FastAPI Tutorials
Published in
5 min readMar 11, 2021
Photo by NASA on Unsplash

In this latest installment of FastAPI tutorials, we will focus on integrating FastAPI with a MongoDB database backend.

MongoDB is a document oriented NoSQL database that stores JSON documents. As both MongoDB and FastAPI work natively with JSON, they make a good pair.

Getting Started with MongoDB

To get started, you first need to install the community edition of MongoDB. MongoDB provides complete instructions for all platforms, including Mac OS X, Windows and Linux. Next, it helps to familiarize yourself with the Mongo Shell. You should know enough of the basics to use the Mongo Shell for creating new records and then retrieving them.

It’s also quite helpful to use a GUI for your MongoDB instance. This enables you to browse databases, collections and records, which can quite helpful when debugging your FastAPI application. I recommend Robo 3T — it’s free and easy to use.

Getting Started with PyMongo

PyMongo is the official Python database driver for MongoDB.

To install PyMongo, run:

pip install pymongo

You can then connect to your MongoDB database server via the MongoClient. For example:

from pymongo import MongoClient
client = MongoClient()

With no arguments to MongoClient(), you will connect to the default, local MongoDB server. Once the connection is established, you can drill down to a specific database or a specific collection within the database. For example, let's imagine that we want to store messages for our Slack clone (see Up and Running with FastAPI for a description of the Slack clone). In this case, we will need a slack database, and a collection called messages. We can use the following code to connect:

db = client["slack"]
msg_collection = db["messages"]

The code above should always work. For example, if the database and collection already exist, you will now have the correct references. If either does not yet exist within the database, MongoDB will automatically create them. Note however that MongoDB works in lazy mode, and will not create the database or collection until you actually insert your first document.

Inserting Records

To insert new records, you specify your record as a Python dict, and then use the insert_one() or insert_many() methods. For example, let's define a new message for our Slack code:

# Create a message dict
message = {
"channel": "dev",
"author": "cerami",
"text": "Hello, world!"
}

We can then use the insert_one() method:

result = msg_collection.insert_one(message)
print(result.inserted_id)

If successful, MongoDB will automatically create an ID for your new document, and the result object will include the newly generated inserted_id. You should then receive a notification like this:

Inserted:  604793d07fca43827e384801

Retrieving Records

Retrieving records with PyMongo is just as easy. For example, we can retrieve all the document in msg_collection:

import pprint
pp = pprint.PrettyPrinter(indent=4)
for doc in msg_collection.find():
pp.pprint(doc)

Here I am using the find() argument with no arguments, and also using pprint to pretty print the returned document. You can also pass dict arguments to find() in order to retrieve matching records. For example, you can retrieve all messages within a specific channel:

record_list = msg_collection.find({"channel": "dev"}):

or, all messages written by a specific author.

record_list = msg_collection.find({"author": "cerami"})

Python Unpack Operator

We now know enough PyMongo to start integrating with FastAPI. But, there is one more useful concept we need under our belt — the Python ** unpacking operator!

To understand how the unpacking operator works, let’s consider a simple Pydantic class that stores information about people.

New to Pydantic? Check out FastAPI: Data Models.

from pydantic import BaseModelclass Person(BaseModel):
first: str
last: str
zip_code: str
def __str__(self):
return "%s %s: %s" % (self.first, self.last, self.zip_code)

Here we have a Person class with three attributes, all defined as strings. I have also added a __str__ method for easy printing.

As with our previous Pydantic examples, Person extends BaseModel, and the constructor of BaseModel is defined to take any number of arguments. We can therefore instantiate a Person object like so:

person = Person(first="Bruce", last="Wayne", zip_code="10021")

Pydantic is clever enough to take all our arguments, and assign them to the correct attributes. If we then call print(person), we get:

Bruce Wayne: 10021

Now, let’s suppose we have a dict object like so:

person_dict = {
"first": "Bruce",
"last": "Wayne",
"zip_code": "10021"
}

Further, we now want to use this information to create a new Person object. One option is to unpack everything ourselves. For example:

person = Person(
first=person_dict["first"],
last=person_dict["last"],
zip_code=person_dict["zip_code"])

However, this can make for a whole lot of code. A much more compact option is to use the ** Python unpacking operator. The unpacking operator will automatically unpack all the members of your dictionary, and pass them along for you! This results in much cleaner and flexible code:

person = Person(**person_dict)

The unpacking operator is particularly convenient for FastAPI and MongoDB, because FastAPI works with Pydantic models, and MongoDB works with dict objects. The ** Python unpacking operator provides the necessary glue to easily convert dict objects from MongoDB to Pydantic models needed by FastAPI. We will see additional examples in the next section.

Fast API and MongoDB

With the basics of PyMongo and the Python ** unpacking operator under our belt, we are now ready to start building our FastAPI application. Continuing with the theme of our Slack clone, we will keep the same API endpoints, but now add a MongoDB backend.

Here is the complete code:

There are a few important elements to consider in the code above. Let’s take a closer look:

  1. We have our standard Message class that extends the Pydantic BaseModel.
  2. The get_message_collection() method connects to the MongoDB database, and retrieves the messages collection from the slack database.
  3. The get_channels() method leverages the PyMongo distinct() method to retrieve the list of distinct channels in the message collection.
  4. The get_messages() method leverages the ** unpacking operator to easily convert from MongoDB dict objects to Pydantic-based Message objects.

Note: As I have since discovered, this is not the most performant option for managing your connections to MongoDB. For a better option, see my blog post on Benchmarking FastAPI and MongoDB Options.

To run the code above, first make sure that your MongoDB server is running. Then, as with our previous FastAPI examples, you can run the application via uvicorn:

uvicorn slack:app --reload

And, with that, you are up and running with FastAPI and MongoDB!

--

--

Ethan Cerami
FastAPI Tutorials

Director, Knowledge Systems Group @ Dana-Farber Cancer Institute, Boston MA.