DEV Community

Cover image for Building a CRUD API with FastAPI and MongoDB
Mohd Aquib
Mohd Aquib

Posted on • Edited on

Building a CRUD API with FastAPI and MongoDB

Welcome, fellow developers! In this blog post, we'll dive into the exciting world of building RESTful APIs with FastAPI, a modern, fast, and easy-to-use framework, and MongoDB, a powerful NoSQL database. We'll create a simple CRUD (Create, Read, Update, Delete) API for managing a collection of books. Let's get started!

1. Setting the Stage:

  • Install Dependencies: Begin by installing the required libraries:
  pip install fastapi uvicorn pymongo[srv]
Enter fullscreen mode Exit fullscreen mode
  • Create Project Structure: Organize your project with a clear structure:
  my-book-api/
  ├── main.py
  ├── models.py
  └── schemas.py
Enter fullscreen mode Exit fullscreen mode

2. Defining Our Book Model:

We'll start by defining our book model in models.py. This model represents the structure of our book data:

from pydantic import BaseModel

class Book(BaseModel):
    title: str
    author: str
    description: str
    published_year: int

    class Config:
        schema_extra = {
            "example": {
                "title": "The Hitchhiker's Guide to the Galaxy",
                "author": "Douglas Adams",
                "description": "A humorous science fiction novel.",
                "published_year": 1979
            }
        }
Enter fullscreen mode Exit fullscreen mode

3. Database Connection:

In main.py, we'll establish a connection to our MongoDB database:

from fastapi import FastAPI
from pymongo import MongoClient

app = FastAPI()

client = MongoClient("mongodb+srv://<your_username>:<your_password>@<your_cluster>.mongodb.net/<your_database>?retryWrites=true&w=majority")
db = client["my_book_database"]
collection = db["books"]
Enter fullscreen mode Exit fullscreen mode

4. Creating the CRUD Operations:

Now, let's define our CRUD endpoints within main.py:

4.1. Creating a Book (POST):

@app.post("/books", response_model=Book)
async def create_book(book: Book):
  book_dict = book.dict()
  collection.insert_one(book_dict)
  return book
Enter fullscreen mode Exit fullscreen mode

4.2. Reading Books (GET):

@app.get("/books")
async def get_books():
  books = []
  for book in collection.find():
    books.append(Book(**book))
  return books
Enter fullscreen mode Exit fullscreen mode

4.3. Getting a Specific Book (GET):

@app.get("/books/{book_id}", response_model=Book)
async def get_book(book_id: str):
  book = collection.find_one({"_id": ObjectId(book_id)})
  if book:
    return Book(**book)
  else:
    raise HTTPException(status_code=404, detail="Book not found")
Enter fullscreen mode Exit fullscreen mode

4.4. Updating a Book (PUT):

@app.put("/books/{book_id}", response_model=Book)
async def update_book(book_id: str, book: Book):
  book_dict = book.dict()
  collection.update_one({"_id": ObjectId(book_id)}, {"$set": book_dict})
  return book
Enter fullscreen mode Exit fullscreen mode

4.5. Deleting a Book (DELETE):

@app.delete("/books/{book_id}")
async def delete_book(book_id: str):
  collection.delete_one({"_id": ObjectId(book_id)})
  return {"message": "Book deleted successfully"}
Enter fullscreen mode Exit fullscreen mode

5. Running the API:

Finally, run the API using uvicorn:

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode

6. Testing the API:

You can test your API using tools like Postman or curl. For example, to create a new book:

curl -X POST -H "Content-Type: application/json" -d '{"title": "The Lord of the Rings", "author": "J.R.R. Tolkien", "description": "A classic fantasy novel.", "published_year": 1954}' http://localhost:8000/books
Enter fullscreen mode Exit fullscreen mode

Tips and Tricks:

  • Use a .env file: Store sensitive information like database credentials in a .env file.
  • Implement error handling: Handle potential errors with appropriate error messages and status codes.
  • Document your API: Use tools like Swagger or OpenAPI to create documentation for your API.
  • Consider using a database driver for more robust connections and operations.

Conclusion:

Creating CRUD operations with FastAPI and MongoDB is a simple yet powerful way to build RESTful APIs. By following these steps, you can quickly get your API up and running. Remember to explore additional features like pagination, filtering, and authentication for a more robust and user-friendly API. Happy coding!

Top comments (3)

Collapse
 
praveensoniofficial profile image
Praveen Soni • Edited

I want to see main file contents

Collapse
 
aquibpy profile image
Mohd Aquib

Here is the complete content of the main.py file based on the steps provided in the blog post:

from fastapi import FastAPI, HTTPException
from pymongo import MongoClient
from bson.objectid import ObjectId
from models import Book

app = FastAPI()

# Establish a connection to the MongoDB database
client = MongoClient("mongodb+srv://<your_username>:<your_password>@<your_cluster>.mongodb.net/<your_database>?retryWrites=true&w=majority")
db = client["my_book_database"]
collection = db["books"]

# Create a new book
@app.post("/books", response_model=Book)
async def create_book(book: Book):
    book_dict = book.dict()
    collection.insert_one(book_dict)
    return book

# Get a list of all books
@app.get("/books")
async def get_books():
    books = []
    for book in collection.find():
        books.append(Book(**book))
    return books

# Get a specific book by its ID
@app.get("/books/{book_id}", response_model=Book)
async def get_book(book_id: str):
    book = collection.find_one({"_id": ObjectId(book_id)})
    if book:
        return Book(**book)
    else:
        raise HTTPException(status_code=404, detail="Book not found")

# Update a book by its ID
@app.put("/books/{book_id}", response_model=Book)
async def update_book(book_id: str, book: Book):
    book_dict = book.dict()
    collection.update_one({"_id": ObjectId(book_id)}, {"$set": book_dict})
    return book

# Delete a book by its ID
@app.delete("/books/{book_id}")
async def delete_book(book_id: str):
    collection.delete_one({"_id": ObjectId(book_id)})
    return {"message": "Book deleted successfully"}
Enter fullscreen mode Exit fullscreen mode

Make sure to replace <your_username>, <your_password>, <your_cluster>, and <your_database> with your actual MongoDB credentials and database information.

Additionally, here is the content of models.py which defines the Book model:

from pydantic import BaseModel

class Book(BaseModel):
    title: str
    author: str
    description: str
    published_year: int

    class Config:
        schema_extra = {
            "example": {
                "title": "The Hitchhiker's Guide to the Galaxy",
                "author": "Douglas Adams",
                "description": "A humorous science fiction novel.",
                "published_year": 1979
            }
        }
Enter fullscreen mode Exit fullscreen mode

Feel free to let me know if you need any further details.

Collapse
 
praveensoniofficial profile image
Praveen Soni

Thanks bro!