An API with Python and FastAPI

TeselaDB

TeselaDB

TeselaDB team

25 minBeginner

Build an orders API with FastAPI and PyMongo that validates each order and saves it in TeselaDB.

A Python API uses TeselaDB with PyMongo, the official MongoDB driver. Here you write with FastAPI the same orders API as in the Express guide, which creates an order and reads it.

What you will build

A FastAPI server on port 8000 with POST /orders, which validates the order with Pydantic and saves it, and GET /orders/{order_id}, which returns it or answers 404.

Requirements

  • Python 3.9 or later.
  • A TeselaDB cluster and a user with readWrite on the shop database.
  • The connection string with TLS in the TESELADB_URI environment variable, as in Python.

1. Create the environment

Shell
python3 -m venv .venv
. .venv/bin/activate
pip install fastapi uvicorn pymongo

2. Write the API

Save it as main.py. The endpoint functions are synchronous, so FastAPI runs them in its thread pool and the PyMongo client shares its connections between them:

Python
import os
from datetime import datetime, timezone

from bson import ObjectId
from bson.errors import InvalidId
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from pymongo import MongoClient

client = MongoClient(os.environ["TESELADB_URI"])
orders = client.shop.orders
app = FastAPI()


class NewOrder(BaseModel):
    customer: str
    total: float = Field(ge=0)


@app.post("/orders", status_code=201)
def create_order(order: NewOrder):
    result = orders.insert_one({**order.model_dump(), "createdAt": datetime.now(timezone.utc)})
    return {"id": str(result.inserted_id)}


@app.get("/orders/{order_id}")
def read_order(order_id: str):
    try:
        order = orders.find_one({"_id": ObjectId(order_id)})
    except InvalidId:
        order = None
    if order is None:
        raise HTTPException(status_code=404)
    order["_id"] = str(order["_id"])
    return order

Try it

Start the server with uvicorn main:app. Then, in another terminal, create an order for Ana with a total of 30 and read it back with the id that the first request returns, which goes in place of <id> in the second command:

Shell
curl -s -X POST localhost:8000/orders -H "content-type: application/json" -d '{"customer":"Ana","total":30}'
curl -s localhost:8000/orders/<id>

The first command returns {"id":"…"} and the second, the order with its creation date. An order with a negative total answers 422 with the validation error.

Clean up

Stop the server with Ctrl+C and delete the test orders from mongosh:

JavaScript
db.getSiblingDB("shop").orders.deleteMany({ customer: "Ana" });

Next steps