Quizlet-like platform for studying for professional certification/licensing exams, focused on but not limited to state regulated licensing (i.e. Bar Exam, USMLE, CPA, etc).
The backend is a layered FastAPI app:
server.pybootstraps FastAPI, CORS, and mounts routers.routes/defines HTTP endpoints and request auth requirements.controllers/handles request orchestration and maps domain errors to HTTP errors.services/contains business logic and throws typed app-level errors (AppError).schemas/defines request/response contracts with Pydantic models.
Current entrypoint:
src/server/server.py- Mounts
jwt_auth.services.auth_services(fromjwt-authdependency) - Mounts local business routes from
src/server/routes/example_route.py
- Client calls a route in
routes/*. - Route validates input via
schemas/*and validates token withdecode_access_token. - Route calls controller.
- Controller calls service.
- Service returns data or raises
AppError. - Controller converts
AppErrorintoHTTPExceptionwith the right status code.
Create env files:
- Copy
src/server/.env.exampleinto:src/server/.env.devsrc/server/.env.prod
- Fill in DB and JWT values.
Run backend locally with Docker Compose:
cd src/infrastructure
docker compose up --build server redisHealth check:
GET http://127.0.0.1:8000/->{ "status": "healthy" }
Run full local stack (client + server + redis):
cd src/infrastructure
docker compose up --buildUseful Docker commands:
# stop containers
docker compose down
# restart only backend services
docker compose up --build -d server redis
# backend logs
docker compose logs -f serverUse this order for every feature.
Create a request model in src/server/schemas/schema.py (or a new schema module):
from pydantic import BaseModel
from typing import Optional
class CreateThingRequest(BaseModel):
name: str
description: Optional[str] = NoneAdd pure business logic in src/server/services/service.py (or a feature-specific service file):
class AppError(Exception):
def __init__(self, message: str, status_code: int = 400):
self.message = message
self.status_code = status_code
super().__init__(message)
def create_thing_service(name: str):
if not name:
raise AppError("name is required", 400)
return {"name": name}Controllers call services and normalize errors:
from fastapi import HTTPException
from services.service import create_thing_service, AppError
def create_thing_controller(request):
try:
return create_thing_service(request.name)
except AppError as error:
raise HTTPException(detail=error.message, status_code=error.status_code)Expose the endpoint in src/server/routes/:
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordBearer
from jwt_auth.services.auth_services import decode_access_token
from controllers.example_controller import create_thing_controller
import schemas.schema as schema
router = APIRouter()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
@router.post("/things")
def create_thing(request: schema.CreateThingRequest, token: str = Depends(oauth2_scheme)):
decode_access_token(token)
return create_thing_controller(request)In src/server/server.py, import and mount the new router:
from routes.things_route import router as things_router
app.include_router(things_router)- Protected routes use
OAuth2PasswordBearer(tokenUrl="/auth/login"). - The bearer token is validated via
decode_access_token(token). - Invalid tokens return
401automatically from the auth helper.
src/server/data/contains source exam JSON and parsing utilities.src/server/controllers/example_controller.pyincludesupdate_Database(path)for loading JSON questions into Upstash Vector DB using OpenAI embeddings.- For production, prefer API-based secret loading over interactive
getpassprompts.
Branching model:
prod: stable production branchtesting: integration and pre-release testingfeature/*: individual feature/fix branches
Workflow:
- Branch from
testing. - Implement and commit scoped changes.
- Merge latest
testinginto your feature branch before PR. - Open PR from
feature/*->testing(Or pullfeature/*into testing). - CI promotes
testing->prodafter checks pass.