I am new to the FastApi world. Can somebody review my below Fast API design? It is working but doesn't follow FastApi conventions. I find it much cleaner and follows layered architecture taking inspiration from Java Spring-boot landscape. What are pros and cons of this desing.
routes -> services -> crud -> models and using schemas.
Can like Springboot I can have something like @Autowired
plant_model.py
from sqlalchemy import Column, Integer, String, Text
from app.database.postgres import Base
class Plant(Base):
__tablename__ = "plant"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
type = Column(String)
description = Column(Text)
plant_schemas.py
from pydantic import BaseModel
class PlantBase(BaseModel):
name: str
type: str
class PlantCreate(PlantBase):
pass
class PlantInDB(PlantBase):
id: int
class PlantPublic(PlantInDB):
pass
plant_crud.py
from typing import List
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.database.postgres import SessionLocal
from .plant_schemas import PlantCreate, PlantInDB
from .plant_models import Plant
class PlantCrud:
def __init__(self, db: Session = SessionLocal()) -> None:
self.db = db
def create_plant(self, plantCreate: PlantCreate):
db_plant = Plant(plantCreate.model_dump())
self.db.add(db_plant)
self.db.commit()
return db_plant
def get_plants(self, skip: int = 0, limit: int = 100):
return self.db.query(Plant).offset(skip).limit(limit).all()
plant_service.py
from fastapi import Depends
from app.api.plant.plant_crud import PlantCrud
from app.api.plant.plant_schemas import PlantCreate
class PlantService:
def __init__(self, plantCrud: PlantCrud = PlantCrud()) -> None:
self.plantCrud = plantCrud
def create_plant(self, plantCreate: PlantCreate):
return self.plantCrud.create_plant(plantCreate)
def get_plants(self, skip: int = 0, limit: int = 100):
return self.plantCrud.get_plants(skip, limit)
plant_routes.py
from app.api.plant.plant_models import Plant
from app.api.plant.plant_schemas import PlantCreate, PlantInDB
from app.api.plant.plant_service import PlantService
from fastapi import Depends, APIRouter, HTTPException
from fastapi import APIRouter, Depends
router = APIRouter()
class PlantRoutes:
def __init__(self, plantService: PlantService = PlantService()) -> None:
self.plant_service = plantService
def get_plants(self):
return self.plant_service.get_plants()
def create_plant(self, plantCreate: PlantCreate):
return self.plant_service.create_plant(plantCreate)
plantRoutes = PlantRoutes()
router.add_api_route("/plants", plantRoutes.get_plants, methods=["GET"], response_model=list[PlantInDB])
router.add_api_route("/plants", plantRoutes.create_plant, methods=["POST"], response_model=PlantInDB, status_code=201)
main.py
# app/__init__.py
from fastapi import FastAPI
import os
import sys
from fastapi.middleware.cors import CORSMiddleware
current_script_dir = os.path.dirname(os.path.realpath(__file__))
# Add the parent directory (project directory) to sys.path
sys.path.append(os.path.dirname(current_script_dir))
my_app = FastAPI()
origins = [
"*"
]
my_app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
from app.api.plant.plant_routes import router as plant_router
my_app.include_router(plant_router, prefix="/plant", tags=["plant"])
if __name__ == "__main__":
import uvicorn
from main import my_app
uvicorn.run("main:my_app", host="127.0.0.1", reload=True)
```
SQLAlchemyandPydanticas part of your study you should checkout SQLModel \$\endgroup\$