Skip to content

Optimized version#2

Merged
yuting1214 merged 6 commits intomainfrom
optimized-version
Feb 20, 2026
Merged

Optimized version#2
yuting1214 merged 6 commits intomainfrom
optimized-version

Conversation

@yuting1214
Copy link
Copy Markdown
Owner

Optimized Version: Performance, Structure, and Best Practices Overhaul

Summary

This branch transforms the FastAPI fullstack template from a basic prototype into a production-ready, fully async application with modern Python tooling and FastAPI best practices.

Commits included

  • 3a9cc72 [FEAT] Add locust file to benchmark performance
  • 6c1f6fa [EHN] orjson, lifespan thread pool, middleware refactor, gzip compression
  • 7e5aa79 Merge resolved keeping our changes
  • bf667bc [EHN] Add locust in dependency
  • 7af624c [REFACTOR] Restructure project, migrate to uv, apply FastAPI best practices

What Changed

1. Performance Optimizations (early commits)

  • ORJSONResponse as default response class for faster JSON serialization
  • Increased thread pool from default 40 → 100 via anyio limiter in lifespan
  • GZIP compression middleware for responses > 1KB
  • httptools + uvloop for faster HTTP parsing and event loop
  • Locust load testing configuration for benchmarking all CRUD endpoints

2. Project Restructure

  • src/ layout: backend/fastapi/src/backend/, frontend/src/frontend/ — removes redundant nesting, follows standard Python packaging conventions
  • Root tests/ directory with shared conftest.py for DB lifecycle management
  • Removed dead files: constants.py, authorization.py, rate_limiter.py, root __init__.py

3. Package Management: requirements.txtuv

  • pyproject.toml with separated core and dev dependencies ([project.optional-dependencies])
  • uv.lock for reproducible installs
  • Python 3.12 pinned via .python-version
  • Dropped unused deps: trio, databases (SQLAlchemy async handles everything natively)

4. Environment & Security

  • .env.env.example: Secrets no longer tracked in git
  • .gitignore updated: Covers .env, .env.*, .cursor/, .claude/, .DS_Store
  • Hardcoded SECRET_KEY fixed: Session middleware reads from pydantic-settings instead of "your_secret_key"
  • Authentication centralized: Uses global_settings instead of scattered os.getenv() calls

5. FastAPI Best Practices

Integrations from Kludex/fastapi-tips and FastAPI---Strawberry:

Change Source
All endpoints & deps are async def (no thread pool waste) Tips #2, #9
Pure ASGI DocProtectMiddleware (no BaseHTTPMiddleware overhead) Tip #8
Typed lifespan state yielding AppState(TypedDict) with session factory Tip #6
uvloop added as platform-conditional dependency Tip #1
All-async tests with httpx.AsyncClient + pytest-anyio Tips #5, #10
Async DB init via conn.run_sync(Base.metadata.create_all) FastAPI---Strawberry
Proper engine disposal on shutdown FastAPI---Strawberry
SQLAlchemy 2.0: select(), DeclarativeBase, Mapped, async_sessionmaker SQLAlchemy docs
Portable UUID as String(36) for SQLite + PostgreSQL compat
Removed duplicate /messages/async endpoint (all endpoints are now async)

6. Testing

  • Consolidated: test_api_sync.py + test_api_async.py → single test_api.py
  • conftest.py: Shared fixtures handle DB creation/teardown since ASGITransport does not trigger FastAPI lifespan events
  • All 7 tests pass locally and inside Docker container

7. Deployment

  • Dockerfile: Python 3.12 + uv (replaces pip + requirements.txt)
  • Docker verified: docker build + docker run ... pytest — 7/7 pass

Before / After

Area Before After
Project layout backend/fastapi/ (redundant) src/backend/ (clean)
Package manager requirements.txt (no pinning) uv + pyproject.toml + lockfile
Python version 3.9 3.12
Database access Mixed sync + async, session.query() Fully async, select()
Middleware BaseHTTPMiddleware wrapper Pure ASGI middleware
Lifespan No state, sync init, no cleanup Typed state, async init, engine disposal
Secret key Hardcoded "your_secret_key" Environment-driven SECRET_KEY
Dependencies Unused trio, databases Lean, purposeful
Testing Mixed sync/async, separate files All async, unified, conftest.py lifecycle
Env management Empty .env in git .env.example template, .env gitignored

Test Plan

  • uv run pytest tests/ -v — 7/7 pass (0 warnings)
  • uv run ruff check — all checks passed
  • docker build -t fullstack-fastapi . — builds successfully
  • docker run --rm fullstack-fastapi sh -c "uv sync --extra dev && uv run pytest tests/ -v" — 7/7 pass in container
  • Manual smoke test: uv run python -m src.backend.main --mode dev and verify /docs, /login, CRUD via Swagger
  • Load test: uv run locust against running server

yuting1214 and others added 5 commits April 5, 2025 21:59
…refactor middleware and add gzip compression 4.
…ctices

Project structure:
- Reorganize from backend/fastapi/ to src/backend/, frontend/ to src/frontend/
- Move tests to root tests/ directory with shared conftest.py
- Remove dead files (constants.py, authorization.py, rate_limiter.py)

Package management:
- Replace requirements.txt with pyproject.toml + uv
- Pin Python 3.12 via .python-version
- Drop unused deps (trio, databases), add uvloop

Environment & security:
- Replace tracked .env with .env.example template
- Add .env, .cursor/, .claude/ to .gitignore
- Fix hardcoded SECRET_KEY in SessionMiddleware
- Auth reads from pydantic-settings instead of os.getenv()

FastAPI best practices (from Kludex/fastapi-tips):
- Convert all endpoints and CRUD to async (Tips #2, #9)
- Pure ASGI DocProtectMiddleware replacing BaseHTTPMiddleware (Tip #8)
- Typed lifespan state yielding session factory (Tip #6)
- Async DB init and proper engine disposal on shutdown
- SQLAlchemy 2.0: select(), DeclarativeBase, Mapped, async_sessionmaker
- Portable UUID column (String(36)) for SQLite + PostgreSQL compat

Testing:
- All-async test suite with httpx.AsyncClient + pytest-anyio (Tips #5, #10)
- conftest.py handles DB lifecycle since ASGITransport skips lifespan
- Consolidated test_api_sync.py + test_api_async.py into test_api.py

Deployment:
- Dockerfile updated for Python 3.12 + uv
- Docker build and test run verified in container (7/7 pass)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@railway-app railway-app bot temporarily deployed to luminous-presence / production February 20, 2026 06:55 Inactive
Replace String(36) with SQLAlchemy 2.0 native Uuid type which maps to
PostgreSQL UUID and SQLite CHAR(32) automatically. Fixes startup crash
when deploying against an existing PostgreSQL database that has UUID
columns from the previous schema.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@railway-app railway-app bot temporarily deployed to luminous-presence / production February 20, 2026 07:06 Inactive
@yuting1214 yuting1214 merged commit 47f036b into main Feb 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant