You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`FastAPI <https://fastapi.tiangolo.com/>`__ is a modern, high-performance, easy-to-learn, fast-to-code, production-ready, Python 3.6+ framework for building APIs based on standard Python type hints. While it might not be as established as some other Python frameworks such as Django, it is already in production at companies such as Uber, Netflix, and Microsoft.
22
26
23
27
FastAPI is async, and as its name implies, it is super fast; so, MongoDB is the perfect accompaniment. In this quick start, we will create a CRUD (Create, Read, Update, Delete) app showing how you can integrate MongoDB with your FastAPI projects.
@@ -29,7 +33,7 @@ Prerequisites
29
33
- A MongoDB Atlas cluster. Follow the "`Get Started with Atlas <https://docs.atlas.mongodb.com/getting-started/>`__" guide to create your account and MongoDB cluster. Keep a note of your username, password, and `connection string <https://docs.atlas.mongodb.com/tutorial/connect-to-your-cluster/#connect-to-your-atlas-cluster>`__` as you will need those later.
30
34
31
35
Running the Example
32
-
___________________
36
+
-------------------
33
37
34
38
To begin, you should `clone the example code from GitHub <https://github.com/mongodb-developer/mongodb-with-fastapi>`__.
35
39
@@ -63,7 +67,7 @@ The final step is to start your FastAPI server.
We're using the async `motor driver <https://motor.readthedocs.io/en/stable/>`__ to create our MongoDB client, and then we specify our database name `college`.
93
+
We're using the async `motor driver <https://motor.readthedocs.io/en/stable/>`__ to create our MongoDB client, and then we specify our database name ``college``.
90
94
91
95
The _id Attribute and ObjectIds
92
96
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -97,12 +101,12 @@ The _id Attribute and ObjectIds
97
101
# It will be represented as a `str` on the model so that it can be serialized to JSON.
98
102
PyObjectId = Annotated[str, BeforeValidator(str)]
99
103
100
-
MongoDB stores data as `BSON <https://www.mongodb.com/json-and-bson>`__. FastAPI encodes and decodes data as JSON strings. BSON has support for additional non-JSON-native data types, including `ObjectId` which can't be directly encoded as JSON. Because of this, we convert `ObjectId`s to strings before storing them as the `id` field.
104
+
MongoDB stores data as `BSON <https://www.mongodb.com/json-and-bson>`__. FastAPI encodes and decodes data as JSON strings. BSON has support for additional non-JSON-native data types, including ``ObjectId`` which can't be directly encoded as JSON. Because of this, we convert ``ObjectId``s to strings before storing them as the ``id`` field.
101
105
102
106
Database Models
103
107
~~~~~~~~~~~~~~~
104
108
105
-
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. Our application has three models, the `StudentModel`, the `UpdateStudentModel`, and the `StudentCollection`.
109
+
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. Our application has three models, the ``StudentModel``, the ``UpdateStudentModel``, and the ``StudentCollection``.
106
110
107
111
.. code-block:: python
108
112
@@ -113,7 +117,7 @@ Many people think of MongoDB as being schema-less, which is wrong. MongoDB has
113
117
114
118
# The primary key for the StudentModel, stored as a `str` on the instance.
115
119
# This will be aliased to `_id` when sent to MongoDB,
116
-
# but provided as `id` in the API requests and responses.
120
+
# but provided as ``id`` in the API requests and responses.
@@ -134,9 +138,9 @@ Many people think of MongoDB as being schema-less, which is wrong. MongoDB has
134
138
135
139
This is the primary model we use as the `response model <https://fastapi.tiangolo.com/tutorial/response-model/>`__ for the majority of our endpoints.
136
140
137
-
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 <https://pydantic-docs.helpmanual.io/>`__—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 `populate_by_name` to `True` in the model's `model_config`
141
+
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 <https://pydantic-docs.helpmanual.io/>`__—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 `populate_by_name` to `True` in the model's `model_config`
138
142
139
-
We set this `id` value automatically to `None`, so you do not need to supply it when creating a new student.
143
+
We set this ``id`` value automatically to `None`, so you do not need to supply it when creating a new student.
140
144
141
145
.. code-block:: python
142
146
@@ -164,7 +168,7 @@ We set this `id` value automatically to `None`, so you do not need to supply it
164
168
165
169
The `UpdateStudentModel` has two key differences from the `StudentModel`:
166
170
167
-
- It does not have an `id` attribute as this cannot be modified.
171
+
- It does not have an ``id`` attribute as this cannot be modified.
168
172
- All fields are optional, so you only need to supply the fields you wish to update.
169
173
170
174
Finally, `StudentCollection` is defined to encapsulate a list of `StudentModel` instances. In theory, the endpoint could return a top-level list of StudentModels, but there are some vulnerabilities associated with returning JSON responses with top-level lists.
@@ -207,7 +211,7 @@ Create Student Route
207
211
"""
208
212
Insert a new student record.
209
213
210
-
A unique `id` will be created and provided in the response.
214
+
A unique ``id`` will be created and provided in the response.
The `create_student` route receives the new student data as a JSON string in a `POST` request. We have to decode this JSON request body into a Python dictionary before passing it to our MongoDB client.
224
+
The ``create_student`` route receives the new student data as a JSON string in a ``POST`` request. We have to decode this JSON request body into a Python dictionary before passing it to our MongoDB client.
221
225
222
-
The `insert_one` method response includes the `_id` of the newly created student (provided as `id` because this endpoint specifies `response_model_by_alias=False` in the `post` decorator call. After we insert the student into our collection, we use the `inserted_id` to find the correct document and return this in our `JSONResponse`.
226
+
The ``insert_one`` method response includes the ``_id`` of the newly created student (provided as ``id`` because this endpoint specifies ``response_model_by_alias=False`` in the ``post`` decorator call. After we insert the student into our collection, we use the ``inserted_id`` to find the correct document and return this in our ``JSONResponse``.
223
227
224
-
FastAPI returns an HTTP `200` status code by default; but in this instance, a `201` created is more appropriate.
228
+
FastAPI returns an HTTP ``200`` status code by default; but in this instance, a ``201`` created is more appropriate.
225
229
226
230
Read Routes
227
231
+++++++++++
@@ -244,7 +248,7 @@ The application has two read routes: one for viewing all students and the other
Motor's `to_list` method requires a max document count argument. For this example, I have hardcoded it to `1000`; but in a real application, you would use the `skip and limit parameters <https://pymongo.readthedocs.io/en/3.11.0/api/pymongo/collection.html#pymongo.collection.Collection.find) in `find` to paginate your results.
251
+
Motor's ``to_list`` method requires a max document count argument. For this example, I have hardcoded it to ``1000``; but in a real application, you would use the `skip and limit parameters <https://pymongo.readthedocs.io/en/3.11.0/api/pymongo/collection.html#pymongo.collection.Collection.find>`__ in ``find`` to paginate your results.
248
252
249
253
.. code-block:: python
250
254
@@ -265,9 +269,9 @@ Motor's `to_list` method requires a max document count argument. For this exampl
265
269
266
270
raise HTTPException(status_code=404, detail="Student {id} not found")
267
271
268
-
The student detail route has a path parameter of `id`, which FastAPI passes as an argument to the `show_student` function. We use the `id` to attempt to find the corresponding student in the database. The conditional in this section is using an `assignment expression <https://www.python.org/dev/peps/pep-0572/>`__, an addition to Python 3.8 and often referred to by the cute sobriquet "walrus operator."
272
+
The student detail route has a path parameter of ``id``, which FastAPI passes as an argument to the ``show_student`` function. We use the ``id`` to attempt to find the corresponding student in the database. The conditional in this section is using an `assignment expression <https://www.python.org/dev/peps/pep-0572/>`__, an addition to Python 3.8 and often referred to by the cute sobriquet "walrus operator."
269
273
270
-
If a document with the specified `_id` does not exist, we raise an `HTTPException` with a status of `404`.
274
+
If a document with the specified ``_id`` does not exist, we raise an ``HTTPException`` with a status of ``404``.
271
275
272
276
Update Route
273
277
++++++++++++
@@ -308,11 +312,11 @@ Update Route
308
312
309
313
raise HTTPException(status_code=404, detail=f"Student {id} not found")
310
314
311
-
The `update_student` route is like a combination of the `create_student` and the `show_student` routes. It receives the `id` of the document to update as well as the new data in the JSON body. We don't want to update any fields with empty values; so, first of all, we iterate over all the items in the received dictionary and only add the items that have a value to our new document.
315
+
The ``update_student`` route is like a combination of the ``create_student`` and the ``show_student`` routes. It receives the ``id`` of the document to update as well as the new data in the JSON body. We don't want to update any fields with empty values; so, first of all, we iterate over all the items in the received dictionary and only add the items that have a value to our new document.
312
316
313
-
If, after we remove the empty values, there are no fields left to update, we instead look for an existing record that matches the `id` and return that unaltered. However, if there are values to update, we use `find_one_and_update <https://motor.readthedocs.io/en/stable/api-asyncio/asyncio_motor_collection.html#motor.motor_asyncio.AsyncIOMotorCollection.find_one_and_update) to `$set <https://docs.mongodb.com/manual/reference/operator/update/set/>`__ the new values, and then return the updated document.
317
+
If, after we remove the empty values, there are no fields left to update, we instead look for an existing record that matches the ``id`` and return that unaltered. However, if there are values to update, we use `find_one_and_update <https://motor.readthedocs.io/en/stable/api-asyncio/asyncio_motor_collection.html#motor.motor_asyncio.AsyncIOMotorCollection.find_one_and_update>`__ to `$set <https://docs.mongodb.com/manual/reference/operator/update/set/>`__ the new values, and then return the updated document.
314
318
315
-
If we get to the end of the function and we have not been able to find a matching document to update or return, then we raise a `404` error again.
319
+
If we get to the end of the function and we have not been able to find a matching document to update or return, then we raise a ``404`` error again.
316
320
317
321
Delete Route
318
322
++++++++++++
@@ -331,17 +335,16 @@ Delete Route
331
335
332
336
raise HTTPException(status_code=404, detail=f"Student {id} not found")
333
337
334
-
Our final route is `delete_student`. Again, because this is acting upon a single document, we have to supply an `id` in the URL. If we find a matching document and successfully delete it, then we return an HTTP status of `204` or "No Content." In this case, we do not return a document as we've already deleted it! However, if we cannot find a student with the specified `id`, then instead we return a `404`.
338
+
Our final route is ``delete_student``. Again, because this is acting upon a single document, we have to supply an ``id`` in the URL. If we find a matching document and successfully delete it, then we return an HTTP status of ``204`` or "No Content." In this case, we do not return a document as we've already deleted it! However, if we cannot find a student with the specified ``id``, then instead we return a ``404``.
335
339
336
340
Our New FastAPI App Generator
337
341
-----------------------------
338
342
339
-
If you're excited to build something more production-ready with FastAPI, React & MongoDB, head over to the `Github repository <https://github.com/mongodb-labs/full-stack-fastapi-mongodb) for our `new FastAPI app generator <https://www.mongodb.com/blog/post/introducing-full-stack-fast-api-app-generator-for-python-developers) and start transforming your web development experience.
343
+
If you're excited to build something more production-ready with FastAPI, React & MongoDB, head over to the `Github repository <https://github.com/mongodb-labs/full-stack-fastapi-mongodb>`__ for our `new FastAPI app generator <https://www.mongodb.com/blog/post/introducing-full-stack-fast-api-app-generator-for-python-developers>`__ and start transforming your web development experience.
340
344
341
345
Wrapping Up
342
346
-----------
343
347
344
348
I hope you have found this introduction to FastAPI with MongoDB useful. If you would like to learn more, check out my post `introducing the FARM stack (FastAPI, React and MongoDB) <https://developer.mongodb.com/how-to/FARM-Stack-FastAPI-React-MongoDB>`__ as well as the `FastAPI documentation <https://motor.readthedocs.io>`__ and this `awesome list <https://github.com/mjhea0/awesome-fastapi>`__.
345
349
346
-
If you have questions, please head to our `developer community website <https://community.mongodb.com/>`__ where MongoDB engineers and the MongoDB community will help you build your next big idea with MongoDB.
347
-
}
350
+
If you have questions, please head to our `developer community website <https://community.mongodb.com/>`__ where MongoDB engineers and the MongoDB community will help you build your next big idea with MongoDB.
0 commit comments