EventGet 50% off your ticket to MongoDB.local NYC on May 2. Use code Web50!Learn more >>
MongoDB Developer
Python
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Languageschevron-right
Pythonchevron-right

Introducing FARM Stack - FastAPI, React, and MongoDB

Aaron Bassett7 min read • Published Feb 05, 2022 • Updated Sep 23, 2022
FastApiJavaScriptPython
Facebook Icontwitter iconlinkedin icon
Rate this article
star-empty
star-empty
star-empty
star-empty
star-empty
When I got my first ever programming job, the LAMP (Linux, Apache, MySQL, PHP) stack—and its variations—ruled supreme. I used WAMP at work, DAMP at home, and deployed our customers to SAMP. But now all the stacks with memorable acronyms seem to be very JavaScript forward. MEAN (MongoDB, Express, Angular, Node.js), MERN (MongoDB, Express, React, Node.js), MEVN (MongoDB, Express, Vue, Node.js), JAM (JavaScript, APIs, Markup), and so on.
As much as I enjoy working with React and Vue, Python is still my favourite language for building back end web services. I wanted the same benefits I got from MERN—MongoDB, speed, flexibility, minimal boilerplate—but with Python instead of Node.js. With that in mind, I want to introduce the FARM stack; FastAPI, React, and MongoDB.

What is FastAPI?

The FARM stack is in many ways very similar to MERN. We've kept MongoDB and React, but we've replaced the Node.js and Express back end with Python and FastAPI. FastAPI is a modern, high-performance, Python 3.6+ web framework. As far as web frameworks go, it's incredibly new. The earliest git commit I could find is from December 5th, 2018, but it is a rising star in the Python community. It is already used in production by the likes of Microsoft, Uber, and Netflix.
And it is speedy. Benchmarks show that it's not as fast as golang's chi or fasthttp, but it's faster than all the other Python frameworks tested and beats out most of the Node.js ones too.

Getting Started

If you would like to give the FARM stack a try, I've created an example TODO application you can clone from GitHub.
The code is organised into two directories: back end and front end. The back end code is our FastAPI server. The code in this directory interacts with our MongoDB database, creates our API endpoints, and thanks to OAS3 (OpenAPI Specification 3). It also generates our interactive documentation.

Running the FastAPI Server

Before I walk through the code, try running the FastAPI server for yourself. You will need Python 3.8+ and a MongoDB database. A free Atlas Cluster will be more than enough. Make a note of your MongoDB username, password, and connection string as you'll need those in a moment.

Installing Dependencies

Configuring Environment Variables

Once you have everything installed and configured, you can run the server with python main.py and visit http://localhost:8000/docs in your browser.
Screencast of CRUD operations via FastAPI docs
This interactive documentation is automatically generated for us by FastAPI and is a great way to try your API during development. You can see we have the main elements of CRUD covered. Try adding, updating, and deleting some Tasks and explore the responses you get back from the FastAPI server.

Creating a FastAPI Server

We initialise the server in main.py; this is where we create our app.
Attach our routes, or API endpoints.
Start the async event loop and ASGI server.
And it is also where we open and close our connection to our MongoDB server.
Because FastAPI is an async framework, we're using Motor to connect to our MongoDB server. Motor is the officially maintained async Python driver for MongoDB.
When the app startup event is triggered, I open a connection to MongoDB and ensure that it is available via the app object so I can access it later in my different routers.

Defining Models

Many people think of MongoDB as being schema-less, which is wrong. MongoDB has a flexible schema. That is to say that collections do not enforce document structure by default, so you have the flexibility to make whatever data-modelling choices best match your application and its performance requirements. So, it's not unusual to create models when working with a MongoDB database.
The models for the TODO app are in backend/apps/todo/models.py, and it is these models which help FastAPI create the interactive documentation.
I want to draw attention to the id field on this model. MongoDB uses _id, but in Python, underscores at the start of attributes have special meaning. If you have an attribute on your model that starts with an underscore, pydantic—the data validation framework used by FastAPI—will assume that it is a private variable, meaning you will not be able to assign it a value! To get around this, we name the field id but give it an alias of _id. You also need to set allow_population_by_field_name to True in the model's Config class.
You may notice I'm not using MongoDB's ObjectIds. You can use ObjectIds with FastAPI; there is just more work required during serialisation and deserialisation. Still, for this example, I found it easier to generate the UUIDs myself, so they're always strings.
When users are updating tasks, we do not want them to change the id, so the UpdateTaskModel only includes the name and completed fields. I've also made both fields optional so that you can update either of them independently. Making both of them optional did mean that all fields were optional, which caused me to spend far too long deciding on how to handle a PUT request (an update) where the user did not send any fields to be changed. We'll see that next when we look at the routers.

FastAPI Routers

The task routers are within backend/apps/todo/routers.py.
To cover the different CRUD (Create, Read, Update, and Delete) operations, I needed the following endpoints:
  • POST /task/ - creates a new task.
  • GET /task/ - view all existing tasks.
  • GET /task/{id}/ - view a single task.
  • PUT /task/{id}/ - update a task.
  • DELETE /task/{id}/ - delete a task.

Create

The create_task router accepts the new task data in the body of the request as a JSON string. We write this data to MongoDB, and then we respond with an HTTP 201 status and the newly created task.

Read

The list_tasks router is overly simplistic. In a real-world application, you are at the very least going to need to include pagination. Thankfully, there are packages for FastAPI which can simplify this process.
While FastAPI supports Python 3.6+, it is my use of assignment expressions in routers like this one, which is why this sample application requires Python 3.8+.
Here, I'm raising an exception if we cannot find a task with the correct id.

Update

We don't want to update any of our fields to empty values, so first of all, we remove those from the update document. As mentioned above, because all values are optional, an update request with an empty payload is still valid. After much deliberation, I decided that in that situation, the correct thing for the API to do is to return the unmodified task and an HTTP 200 status.
If the user has supplied one or more fields to be updated, we attempt to $set the new values with update_one, before returning the modified document. However, if we cannot find a document with the specified id, our router will raise a 404.

Delete

The final router does not return a response body on success, as the requested document no longer exists as we have just deleted it. Instead, it returns an HTTP status of 204 which means that the request completed successfully, but the server doesn't have any data to give you.

The React Front End

The React front end does not change as it is only consuming the API and is therefore somewhat back end agnostic. It is mostly the standard files generated by create-react-app. So, to start our React front end, open a new terminal window—keeping your FastAPI server running in the existing terminal—and enter the following commands inside the front end directory.
These commands may take a little while to complete, but afterwards, it should open a new browser window to http://localhost:3000.
Screenshot of Timeline in browser
The React front end is just a view of our task list, but you can update your tasks via the FastAPI documentation and see the changes appear in React!
Screencast of final TODO app
The bulk of our front end code is in frontend/src/App.js
When our component mounts, we start an interval which runs each second and gets the latest list of tasks before storing them in our state. The function returned at the end of the hook will be run whenever the component dismounts, cleaning up our interval.
The second hook is triggered whenever the task list in our state changes. This hook creates a Timeline Item component for each task in our list.
The last part of App.js is the markup to render the tasks to the page. If you have worked with MERN or another React stack before, this will likely seem very familiar.

Wrapping Up

I'm incredibly excited about the FARM stack, and I hope you are now too. We're able to build highly performant, async, web applications using my favourite technologies! In my next article, we'll look at how you can add authentication to your FARM applications.
In the meantime, check out the FastAPI and Motor documentation, as well as the other useful packages and links in this Awesome FastAPI list.
If you have questions, please head to our developer community website where the MongoDB engineers and the MongoDB community will help you build your next big idea with MongoDB.

Facebook Icontwitter iconlinkedin icon
Rate this article
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Tutorial

Build an App With Python, Flask, and MongoDB to Track UFOs


Feb 06, 2023 | 15 min read
Tutorial

How to Use PyMongo to Connect MongoDB Atlas with AWS Lambda


Jan 23, 2024 | 6 min read
Tutorial

Part #2: Create Your Model Endpoint With Amazon SageMaker, AWS Lambda, and AWS API Gateway


Feb 02, 2024 | 7 min read
Tutorial

Turn BLE: Implementing BLE Sensors with MCU Devkits


Nov 28, 2023 | 13 min read
Table of Contents