Python and FastAPI: Essential Features, Libraries, Packages, and Production Configuration
· 24 min read
When building a real-world Python backend, the goal is not simply to learn Python syntax or FastAPI endpoints. You need to understand the standard application architecture, common libraries, mandatory configurations, and production practices that appear across most backend applications.
We'll use this as our reference architecture. The same concepts apply to ecommerce platforms, SaaS applications, payment systems, internal APIs, and many enterprise backends.
One important distinction before we begin:
- Python language features are capabilities built into Python itself.
- Standard library modules ship with Python.
- Third-party packages must be installed separately.
- Framework features are provided by FastAPI, Starlette, Pydantic, and related libraries.
- Application configuration connects these pieces safely across development, testing, and production.
Not every application needs every library. Some configurations are mandatory for a specific architecture, while others depend on the application's security, deployment, database, and operational requirements.
1. The typical Python backend architecture
Client / Consumer
Mobile app · Web frontend · Other services
FastAPI application layer
Routing · Validation · Dependencies · Authentication · Error handling
Business / application layer
Services · Use cases · Business rules · Transaction boundaries
Data access layer
Repositories · SQLAlchemy · Database sessions
Infrastructure
PostgreSQL · Redis · External APIs · File storage · Message brokers
Across all these layers, the application also needs cross-cutting capabilities:
- Configuration and secrets management
- Logging and error reporting
- Security and access control
- Database migrations
- Automated testing
- Monitoring and health checks
- Deployment and lifecycle management
These cross-cutting capabilities are the focus of this guide.
2. Python language features used frequently in backend applications
Before learning packages, make sure you understand the Python features that libraries and frameworks depend on.
| Python feature | Why it matters | Typical backend use |
|---|---|---|
| Functions | Encapsulate reusable operations | Validation, calculations, utilities |
| Classes and objects | Model behavior and state | Services, repositories, domain objects |
| Type hints | Improve clarity and static analysis | str, int, list[Product] |
dataclass | Convenient data containers | Internal value objects and settings structures |
| Exceptions | Represent and propagate failures | Business errors and database failures |
| Context managers | Manage resource lifecycles | Database sessions, files, locks |
| Iterators and generators | Process data efficiently | Streaming and paginated results |
| Comprehensions | Transform collections concisely | Mapping database records |
| Decorators | Wrap or register behavior | FastAPI routes, caching, instrumentation |
| Modules and packages | Organize code | Feature modules and shared infrastructure |
| Enums | Represent fixed sets of values | Order status, roles, workflow states |
pathlib | Portable filesystem operations | File paths and configuration files |
collections | Specialized data structures | defaultdict, deque, counters |
functools | Reusable function utilities | lru_cache, partial, decorators |
itertools | Efficient iteration tools | Batching and collection processing |
logging | Structured application diagnostics | Errors, warnings, operational events |
json | JSON serialization and parsing | Integrations and file processing |
decimal.Decimal | Exact decimal arithmetic | Monetary values |
uuid | Generate unique identifiers | Request IDs, resource IDs |
datetime | Date/time handling | Timestamps, expiry dates, deadlines |
async / await | Cooperative asynchronous I/O | Database calls and HTTP requests |
typing | Express richer type contracts | Protocol, Literal, TypedDict, Annotated |
unittest | Built-in testing framework | Unit tests when third-party test frameworks aren't needed |
2.1 Type hints and static typing
Example:
def calculate_total( unit_price: Decimal, quantity: int,) -> Decimal: return unit_price * quantity
Type hints help developers, IDEs, and static-analysis tools understand intended input and output types. Python does not automatically enforce all annotations at runtime.
FastAPI uses type annotations to infer request parameters, dependencies, validation rules, and response schemas.
2.2 Exceptions
class ProductNotFoundError(Exception): passdef get_product(product_id: int) -> dict: if product_id <= 0: raise ValueError("Product ID must be positive.") # Real implementation would query a repository. raise ProductNotFoundError( f"Product {product_id} was not found." )
Exceptions should represent meaningful failure conditions. At the API boundary, application exceptions can be translated into appropriate HTTP responses.
2.3 Context managers
from pathlib import Pathwith Path("example.txt").open("w", encoding="utf-8") as file: file.write("BazaarCore")
The with statement manages cleanup even when an operation fails. The same principle applies to database sessions, transactions, and network resources.
2.4 Async and await
async def get_product(product_id: int) -> Product: return await product_repository.get_by_id(product_id)
async and await are particularly useful for I/O-bound work. They do not automatically make CPU-heavy computations faster. An asynchronous function must call compatible asynchronous operations to gain the intended concurrency benefits.
For BazaarCore, async SQLAlchemy and an async HTTP client fit the asynchronous FastAPI architecture.
3. Essential Python standard-library modules
The standard library is included with Python. You do not install these modules separately.
| Module | Common purpose | Example |
|---|---|---|
os | Environment variables and OS interactions | os.getenv() |
sys | Interpreter/runtime information | sys.version |
pathlib | Filesystem paths | Path("app") |
json | JSON encoding and decoding | json.loads() |
logging | Application logging | logging.getLogger() |
datetime | Dates and timestamps | datetime.now(UTC) |
decimal | Precise decimal arithmetic | Decimal("19.99") |
uuid | UUID generation | uuid.uuid4() |
enum | Fixed value sets | class OrderStatus(StrEnum) |
re | Regular expressions | Identifier format validation |
hashlib | Cryptographic hashes | Content fingerprints |
secrets | Secure random tokens | Reset tokens |
hmac | Message authentication | Signature verification |
base64 | Binary-to-text encoding | Encoding payloads |
contextlib | Context manager utilities | Resource management |
functools | Function utilities | lru_cache |
collections | Specialized collections | defaultdict, deque |
itertools | Iterator utilities | Efficient batching |
subprocess | Launch external processes | Running system commands |
shutil | File operations | Copying and archiving |
tempfile | Temporary files | Temporary export generation |
csv | CSV parsing and writing | Bulk product import |
asyncio | Asynchronous task scheduling | Async application primitives |
concurrent.futures | Thread and process pools | Blocking work offloading |
unittest.mock | Mock objects | Isolated unit testing |
Security note: Use secrets rather than random for security-sensitive tokens. Use established password-hashing libraries instead of trying to hash passwords yourself with hashlib.
4. FastAPI features used in most applications
FastAPI is more than a way to define HTTP endpoints. It provides a framework for validating requests, resolving dependencies, producing schemas, and integrating with ASGI middleware.
| Feature | What it does | Example |
|---|---|---|
APIRouter | Organizes endpoints into modules | products_router |
| Path parameters | Capture values from URL paths | /products/{product_id} |
| Query parameters | Filter or configure requests | ?limit=20&offset=0 |
| Request bodies | Receive JSON or other payloads | ProductCreate |
| Pydantic models | Validate and serialize data | ProductResponse |
| Dependency injection | Supply reusable dependencies | Database session |
Depends() | Declare dependencies | Depends(get_db_session) |
Security() | Declare security-related dependencies and scopes | OAuth2 scopes |
| Response models | Define and filter response data | response_model=ProductResponse |
| Status codes | Return correct HTTP semantics | 201, 404, 409 |
| Exception handlers | Standardize error responses | app.add_exception_handler() |
| Middleware | Apply cross-cutting request/response behavior | Request IDs, CORS |
| Lifespan | Manage startup and shutdown resources | Initialize clients and dispose engines |
| Background tasks | Run small post-response tasks | Non-critical notifications |
| File uploads | Accept uploaded files | UploadFile |
| Streaming responses | Stream response data | Large exports |
| WebSockets | Maintain bidirectional connections | Live updates |
| OpenAPI | Generate API schema and docs | /openapi.json |
| Tags and metadata | Organize API documentation | Products, Inventory |
| OAuth2 support | Integrate OAuth2 flows | Bearer token authentication |
Request and Response | Access HTTP request and response details | Headers, cookies |
HTTPException | Raise HTTP errors | HTTPException(404, ...) |
TestClient | Test endpoints | Used later in our testing phase |
4.1 Dependency injection
Dependency injection is one of the most important FastAPI concepts.
from typing import Annotatedfrom fastapi import Dependsfrom sqlalchemy.ext.asyncio import AsyncSessionDatabaseSession = Annotated[ AsyncSession, Depends(get_db_session),]
An endpoint can then declare:
@router.get("/products/{product_id}")async def get_product( product_id: int, session: DatabaseSession,): ...
FastAPI resolves the dependency and passes the session into the endpoint. This reduces manual resource management and makes dependencies reusable.
Typical dependencies include:
- Database sessions
- Authenticated users
- Permission checks
- Pagination parameters
- External API clients
- Tenant or retailer context
4.2 Pydantic v2
Pydantic is a separate package that FastAPI integrates with.
Features you'll use regularly:
BaseModelfor request and response schemasField()for constraints and metadatafield_validatorfor field-level validationmodel_validatorfor cross-field validationConfigDictfor model configurationmodel_validate()for validating Python objectsmodel_dump()for serializing modelsmodel_dump(exclude_unset=True)for partial updatesmodel_validate_json()for validating JSON input
Example:
from pydantic import BaseModel, ConfigDict, Fieldclass ProductResponse(BaseModel): id: int name: str = Field(min_length=2, max_length=250) model_config = ConfigDict(from_attributes=True)
from_attributes=True allows Pydantic to build a response from an ORM object by reading its attributes. It does not automatically load SQLAlchemy relationships; those should be loaded explicitly when needed.
5. Common third-party Python packages
These are the packages most worth learning for a modern backend. The exact stack depends on the application's needs.
5.1 Core application packages
File:Python logo 01.svg - Wikimedia Commons
FastAPI
HTTP API framework
Routes, dependencies, validation integration, OpenAPI, and async endpoint support.
How to Keep LLM Outputs Predictable Using Pydantic Validation
Pydantic + pydantic-settings
Data validation and configuration
Pydantic validates input/output data. pydantic-settings loads and validates environment-based configuration.
SQLAlchemy and the Database Engine — Programming for Financial Technology
SQLAlchemy + Alembic
Database access and schema migrations
SQLAlchemy provides ORM/Core queries and transactions. Alembic tracks database schema changes.
Postgresql Logo PNG Transparent With Clear Background ID 473068 | TOPpng
asyncpg + PostgreSQL
Asynchronous PostgreSQL connectivity and relational storage
asyncpg is a PostgreSQL driver used by SQLAlchemy's async engine.
For BazaarCore, these are our primary dependencies. We'll continue with Pydantic v2 and SQLAlchemy 2.x conventions.
5.2 HTTP and integration packages
| Package | Purpose | When to use |
|---|---|---|
httpx | HTTP client supporting async requests | External catalog APIs, payment providers |
requests | Synchronous HTTP client | Scripts or synchronous applications |
tenacity | Configurable retries | Transient external-service failures |
websockets | WebSocket client/server primitives | Specialized WebSocket integrations |
orjson | Fast JSON serialization | When benchmarks justify it |
python-multipart | Multipart form/file parsing | FastAPI file uploads |
email-validator | Email address validation | Email fields and account registration |
For an async FastAPI application, prefer httpx.AsyncClient for outbound HTTP requests rather than making blocking requests calls directly inside async endpoints.
5.3 Authentication and security packages
| Package | Purpose |
|---|---|
pwdlib | Password hashing integrations; commonly used with Argon2 |
PyJWT | Create and verify JSON Web Tokens |
cryptography | Cryptographic primitives and protocol support |
authlib | OAuth and OpenID Connect integrations |
slowapi | Rate limiting integration for FastAPI/Starlette |
itsdangerous | Signed data and time-limited tokens in suitable workflows |
These are not all mandatory together. For example, JWT is not required if the application uses opaque session tokens. Authentication architecture should determine the packages, not the other way around.
For a production system, also consider:
- HTTPS termination and secure cookie configuration
- Password reset and account recovery
- Token expiration and revocation
- Authorization and tenant isolation
- Secret rotation
- Audit logging for sensitive actions
5.4 Testing and code quality packages
| Package | Purpose |
|---|---|
pytest | Test runner and assertion framework |
pytest-asyncio | Async test support |
httpx | HTTP client used by many FastAPI tests |
coverage / pytest-cov | Measure test coverage |
ruff | Linting and formatting |
mypy or pyright | Static type checking |
pre-commit | Run checks before commits |
factory-boy | Test data factories |
hypothesis | Property-based testing |
respx | Mock HTTPX requests |
We will learn these systematically in the later testing phase, as agreed. For now, you should recognize their roles in a mature Python codebase.
5.5 Logging, observability, and error tracking
| Package | Purpose |
|---|---|
structlog | Structured logging |
python-json-logger | JSON-formatted standard logging |
sentry-sdk | Error and performance monitoring |
opentelemetry-api | Instrumentation interfaces |
opentelemetry-sdk | Telemetry processing |
| OpenTelemetry exporters/instrumentations | Send traces and metrics to observability systems |
prometheus-client | Expose application metrics |
Python's built-in logging module is enough to start. Add structured logging and telemetry as operational requirements grow.
5.6 Caching, background work, and messaging
| Technology/package | Purpose | When to use |
|---|---|---|
redis | Async Redis client support | Caching, counters, coordination |
| Celery | Distributed task processing | Reliable background jobs |
| ARQ | Async Redis-based task queue | Async-oriented background work |
dramatiq | Background task processing | Distributed job workloads |
aio-pika | Async AMQP/RabbitMQ client | Message-driven services |
aiokafka | Async Kafka client | Event streaming and high-volume messaging |
You do not need all of these. Choose one background-work approach that fits the durability, throughput, scheduling, and operational requirements.
5.7 Files, data, and cloud integration
| Package | Purpose |
|---|---|
boto3 / aioboto3 | AWS SDK and async-compatible workflows |
Pillow | Image processing |
openpyxl | Excel workbook processing |
pandas | Tabular data analysis and imports |
pypdf | PDF reading and manipulation |
python-magic | File type identification where supported |
| Cloud-provider SDKs | Object storage, queues, secrets, and other services |
Only install these when the feature requires them. For example, BazaarCore may need image processing and object storage for product photos, but not necessarily at the initial API stage.
6. Which configurations are mandatory?
There is no universal configuration file that every Python application must have. However, a production backend should deliberately address the following areas.
Application configuration
Environment, service name, debug mode, settings validation
Required
Dependency management
Python version, package declarations, reproducible installation
Required
Secrets management
Credentials and keys must not be hardcoded
Required
Database lifecycle
Connection configuration, sessions, transaction policy, migrations
Required if using a database
Error handling
Consistent API errors and safe unexpected-error responses
Required
Logging
Useful operational logs without secrets
Required in production
Security
HTTPS, authentication where needed, authorization, input limits
Required for exposed applications
Health and shutdown
Readiness/liveness checks where relevant, resource cleanup
Required for reliable deployment
Testing and CI
Automated checks before release
Strongly recommended
Monitoring and alerting
Errors, latency, saturation, availability
Strongly recommended in production
Caching and queues
Only if required by performance or background workflows
Conditional
Let's examine the main configuration files and what belongs in each.
6.1 Project and dependency configuration
For a modern Python project, use pyproject.toml as the central configuration file.
It can declare:
- Python version requirements
- Project metadata
- Runtime dependencies
- Development dependencies, depending on the chosen tool
- Ruff settings
- Type-checking configuration
- Pytest configuration
Example for a uv-managed project:
[project]
name = "bazaar-core"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"fastapi",
"uvicorn[standard]",
"pydantic>=2",
"pydantic-settings",
"sqlalchemy[asyncio]>=2",
"asyncpg",
"alembic",
]
[dependency-groups]
dev = [
"pytest",
"pytest-asyncio",
"httpx",
"ruff",
]
[tool.ruff]
line-length = 88
target-version = "py312"
[tool.pytest.ini_options]
testpaths = ["tests"]
This is a starting example, not a complete production lock strategy. Let your package manager resolve compatible versions and commit uv.lock to make installations reproducible.
Install the declared dependencies with:
uv sync --all-groups
Important: pyproject.toml declares project requirements; the lock file records the resolved dependency versions. Both have distinct roles.
6.2 Environment configuration
Different environments require different settings.
| Setting | Development | Production |
|---|---|---|
DEBUG | Usually enabled | Disabled |
| Database | Local/dev database | Managed or production database |
| Logging | Human-readable, detailed | Structured, operationally useful |
| Secret values | Local untracked environment file | Secret manager or protected deployment environment |
| CORS | Local frontend origins | Explicit approved origins |
| Database SQL echo | Sometimes useful | Usually disabled |
| Error responses | Consistent API errors | Never expose tracebacks or secrets |
Create app/core/config.py:
from functools import lru_cachefrom pydantic import Fieldfrom pydantic_settings import BaseSettings, SettingsConfigDictclass Settings(BaseSettings): app_name: str = "BazaarCore" app_version: str = "0.1.0" environment: str = "development" debug: bool = False database_url: str = Field( repr=False, ) log_level: str = "INFO" model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", extra="ignore", )@lru_cachedef get_settings() -> Settings: return Settings()
The @lru_cache decorator ensures settings are loaded once per process when get_settings() is called.
For applications that need environment-specific validation, you can strengthen this configuration with a Literal or enum for environment, stricter URL validation, and explicit production rules.
Use it from the rest of the application:
from app.core.config import get_settingssettings = get_settings()
For a clean configuration architecture, use the cached settings object consistently instead of constructing new Settings() instances in multiple modules.
Example .env
APP_NAME=BazaarCore
APP_VERSION=0.1.0
ENVIRONMENT=development
DEBUG=true
DATABASE_URL=postgresql+asyncpg://bazaar:local_password@localhost:5432/bazaar
LOG_LEVEL=DEBUG
Pydantic Settings maps environment variable names to field names by default, case-insensitively. DATABASE_URL maps to database_url.
Do not commit .env containing secrets. Commit an .env.example with placeholders, and add .env to .gitignore.
In production, environment variables may be injected by your platform, or settings can be retrieved from a secret manager. Avoid baking production secrets into a container image.
6.3 Database configuration
A database-backed FastAPI application normally needs:
- One engine per application process
- A connection pool sized for the deployment
- A session factory
- A request-scoped session dependency
- Explicit transaction boundaries
- Alembic migrations
- Database connectivity and readiness checks
Example:
from collections.abc import AsyncGeneratorfrom sqlalchemy.ext.asyncio import ( AsyncSession, async_sessionmaker, create_async_engine,)from app.core.config import get_settingssettings = get_settings()engine = create_async_engine( settings.database_url, echo=settings.debug, pool_pre_ping=True,)AsyncSessionLocal = async_sessionmaker( engine, class_=AsyncSession, expire_on_commit=False,)async def get_db_session() -> AsyncGenerator[AsyncSession, None]: async with AsyncSessionLocal() as session: yield session
pool_pre_ping=True helps detect stale connections before using them. It does not replace connection timeouts, pool sizing, monitoring, or correct transaction management.
In the application's lifespan handler, dispose of the engine during shutdown:
await engine.dispose()
For BazaarCore, we'll integrate that cleanup into the application's lifespan configuration.
Transaction rule: A session is not the same thing as a transaction. Define which layer owns begin, commit, and rollback. Don't scatter commits across repository methods without a clear policy.
6.4 Logging configuration
Use the built-in logging package from the start.
import logginglogger = logging.getLogger(__name__)def process_product(product_id: int) -> None: logger.info( "Processing product", extra={"product_id": product_id}, )
In production, useful log fields include:
- Timestamp and severity
- Service and environment
- Request or correlation ID
- Route or operation
- Error type
- Duration and relevant resource IDs
Never log passwords, access tokens, full authorization headers, or other sensitive data. Also remember that Python's standard extra fields are not automatically rendered as structured JSON by every logging formatter.
6.5 Exception handling
Our centralized exception handlers are the correct foundation for BazaarCore.
A production API should generally:
- Use a consistent error response schema.
- Map known application exceptions to appropriate status codes.
- Handle request validation errors.
- Avoid leaking unexpected exception details.
- Log unexpected failures with enough context to investigate them.
- Preserve appropriate headers on HTTP exceptions.
Avoid catching every exception and silently returning success-shaped data. Unexpected errors should remain observable.
6.6 Security configuration
The mandatory controls depend on whether the application is public, internal, or restricted to a private network.
For an exposed production API, establish:
- HTTPS between clients and the API, usually through a properly configured ingress or reverse proxy
- Authentication for protected endpoints
- Authorization checks on every sensitive operation
- Input validation and sensible payload-size limits
- CORS configured with explicit allowed origins when browser clients require it
- Rate limiting appropriate to the endpoint
- Secret storage and rotation
- Safe logging and error responses
- Database least-privilege access
CORS is not authentication. A client can call an API without being a browser, so CORS alone never protects business data.
For BazaarCore, tenant isolation is particularly important: a retailer must not access another retailer's inventory simply by guessing an ID.
7. FastAPI application configuration you should standardize
These settings are typically centralized rather than configured independently in every endpoint.
7.1 Lifespan and resource management
Use FastAPI's lifespan mechanism for resources that need initialization and cleanup.
Create or update app/main.py:
from contextlib import asynccontextmanagerfrom fastapi import FastAPIfrom app.core.config import get_settingsfrom app.core.database import enginefrom app.core.exception_handlers import register_exception_handlersfrom app.modules.categories.router import router as categories_routerfrom app.modules.products.router import router as products_routersettings = get_settings()@asynccontextmanagerasync def lifespan(app: FastAPI): # Startup: initialize application-wide resources if needed. yield # Shutdown: release resources owned by this application. await engine.dispose()app = FastAPI( title=settings.app_name, version=settings.app_version, debug=settings.debug, lifespan=lifespan,)register_exception_handlers(app)app.include_router(categories_router, prefix="/api/v1")app.include_router(products_router, prefix="/api/v1")@app.get("/health", tags=["Health"])async def health_check() -> dict[str, str]: return { "status": "ok", "service": settings.app_name, }
This is the basic application-lifecycle pattern. As we add Redis and external API clients, their shared connections can be initialized and closed here as appropriate.
Do not open a new database engine on every request. The engine and connection pool are application-level resources; individual requests receive their own sessions.
7.2 CORS configuration
CORS is needed when a browser-based frontend on one origin calls an API on another origin.
Example:
from fastapi.middleware.cors import CORSMiddlewareapp.add_middleware( CORSMiddleware, allow_origins=[ "http://localhost:3000", "http://localhost:5173", ], allow_credentials=True, allow_methods=["GET", "POST", "PATCH", "DELETE"], allow_headers=["Authorization", "Content-Type"],)
Those origins are development examples. In production, replace them with the approved frontend origins.
If the API doesn't serve cross-origin browser clients, CORS middleware may not be necessary. Never use wildcard origins with credentialed requests.
7.3 Middleware
Middleware runs around request processing and is useful for cross-cutting behavior.
Common middleware or ASGI-level integrations include:
- CORS
- Request/correlation IDs
- Request timing
- Access logging
- Trusted host validation
- HTTPS redirect where appropriate to the deployment
- Compression when justified
- Tracing and metrics instrumentation
Middleware order matters. It can affect which errors are caught, which headers are returned, and whether request IDs are available to exception handlers.
Do not build all application logic into middleware. Authorization, domain validation, and transaction decisions usually belong in dependencies or application services.
7.4 Health and readiness endpoints
A production service often needs to distinguish between:
- Liveness: Is the process alive and able to continue running?
- Readiness: Can the instance currently serve requests, including any required dependency checks?
For a database-backed API, a readiness check may verify that PostgreSQL can execute a trivial query. Avoid making liveness depend on every external service: a temporary database outage should not necessarily cause an orchestrator to restart a healthy process repeatedly.
Our /health endpoint is currently a basic liveness-style response. We can add /health/ready with dependency checks when we configure deployment.
8. A recommended folder structure for production applications
For a modular FastAPI application like BazaarCore, use a structure that separates infrastructure from business modules.
bazaar-core/
├── app/
│ ├── main.py
│ ├── core/
│ │ ├── config.py
│ │ ├── database.py
│ │ ├── security.py
│ │ ├── exceptions.py
│ │ ├── domain_exceptions.py
│ │ ├── exception_handlers.py
│ │ └── logging.py
│ ├── db/
│ │ └── base.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── product.py
│ │ ├── category.py
│ │ └── product_identifier.py
│ ├── modules/
│ │ ├── products/
│ │ │ ├── router.py
│ │ │ ├── schemas.py
│ │ │ ├── service.py
│ │ │ ├── repository.py
│ │ │ └── barcodes.py
│ │ ├── categories/
│ │ │ ├── router.py
│ │ │ ├── schemas.py
│ │ │ ├── service.py
│ │ │ └── repository.py
│ │ └── inventory/
│ └── shared/
├── alembic/
├── tests/
├── .env.example
├── .gitignore
├── .python-version
├── pyproject.toml
├── uv.lock
├── alembic.ini
├── docker-compose.yml
├── Dockerfile
└── README.md
Not every project needs every directory on day one. The structure should follow the application's actual complexity, not become a collection of empty folders.
9. What must be configured before production?
Use this checklist as a practical release gate.
Production readiness checklist
0/20
Application and dependencies
Supported Python version is defined
Runtime dependencies are declared and locked
Development and production settings are separated
Secrets are injected securely and not committed
API and data integrity
Request and response schemas are defined
Centralized exception handlers are registered
Database constraints and migrations are reviewed
Transactions and session cleanup have a clear policy
Security
HTTPS is correctly configured
Authentication and authorization are enforced where required
CORS is restricted to approved browser origins when needed
Input sizes, rate limits, and sensitive logging are addressed
Operations
Structured or otherwise actionable logs are available
Health and readiness checks match deployment needs
Application resources are cleaned up at shutdown
Monitoring and error alerting are configured
Delivery
Automated checks run in CI
Database migration procedure is documented
Container or deployment configuration is reproducible
Backup, restore, and rollback procedures are considered
This is a readiness checklist, not a claim that every item has the same implementation for every deployment. For example, an API that doesn't use a database doesn't need SQLAlchemy or Alembic.
10. Recommended package stack for BazaarCore
For our current project, I recommend this focused set of packages. We should not install every package mentioned above before we need it.
| Area | Package or tool | Priority |
|---|---|---|
| API framework | fastapi | Now |
| ASGI server | uvicorn[standard] | Now |
| Validation and settings | pydantic, pydantic-settings | Now |
| ORM and async database | sqlalchemy[asyncio], asyncpg | Now |
| Database migrations | alembic | Now |
| Environment configuration | Environment variables and local .env | Now |
| Logging | Standard-library logging | Now |
| HTTP integrations | httpx | When external API integration begins |
| Authentication | pwdlib, PyJWT or an appropriate session/token solution | When auth is implemented |
| Linting and formatting | ruff | Early in development |
| Testing | pytest, pytest-asyncio, httpx | Dedicated learning phase |
| Cache | redis | When caching is justified |
| Background processing | Celery, ARQ, or another chosen queue | When asynchronous jobs are needed |
| Observability | OpenTelemetry and/or an error-monitoring service | Before production at an appropriate level |
| Deployment | Docker and CI/CD tooling | Before production deployment |
A reasonable installation command for the packages we already use or are ready to configure is:
uv add fastapi "uvicorn[standard]" pydantic pydantic-settings
uv add "sqlalchemy[asyncio]" asyncpg alembic
uv add httpx
uv add --dev ruff
We will add authentication, caching, queues, and observability dependencies when their features enter the roadmap.
11. What should you learn first?
Given our current progress, follow this order:
- Configuration and application lifecycle:
pydantic-settings, environment variables, lifespan, resource cleanup. - FastAPI request processing: routers, path/query/body parameters, Pydantic schemas, dependency injection, response models.
- Database management: SQLAlchemy sessions, transactions, connection pools, relationships, and Alembic migrations.
- Error handling and logging: application exceptions, HTTP mappings, safe error responses, useful logs.
- Security: authentication, authorization, token lifecycle, tenant isolation, secrets, and CORS.
- External integrations: HTTPX, timeouts, retries, idempotency, provider adapters, and failure handling.
- Production operations: Docker, CI/CD, health checks, monitoring, caching, and background jobs.
- Testing: after understanding the core features, study pytest and apply it throughout the application.