← Back to articles

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 featureWhy it mattersTypical backend use
FunctionsEncapsulate reusable operationsValidation, calculations, utilities
Classes and objectsModel behavior and stateServices, repositories, domain objects
Type hintsImprove clarity and static analysisstr, int, list[Product]
dataclassConvenient data containersInternal value objects and settings structures
ExceptionsRepresent and propagate failuresBusiness errors and database failures
Context managersManage resource lifecyclesDatabase sessions, files, locks
Iterators and generatorsProcess data efficientlyStreaming and paginated results
ComprehensionsTransform collections conciselyMapping database records
DecoratorsWrap or register behaviorFastAPI routes, caching, instrumentation
Modules and packagesOrganize codeFeature modules and shared infrastructure
EnumsRepresent fixed sets of valuesOrder status, roles, workflow states
pathlibPortable filesystem operationsFile paths and configuration files
collectionsSpecialized data structuresdefaultdict, deque, counters
functoolsReusable function utilitieslru_cache, partial, decorators
itertoolsEfficient iteration toolsBatching and collection processing
loggingStructured application diagnosticsErrors, warnings, operational events
jsonJSON serialization and parsingIntegrations and file processing
decimal.DecimalExact decimal arithmeticMonetary values
uuidGenerate unique identifiersRequest IDs, resource IDs
datetimeDate/time handlingTimestamps, expiry dates, deadlines
async / awaitCooperative asynchronous I/ODatabase calls and HTTP requests
typingExpress richer type contractsProtocol, Literal, TypedDict, Annotated
unittestBuilt-in testing frameworkUnit 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.

ModuleCommon purposeExample
osEnvironment variables and OS interactionsos.getenv()
sysInterpreter/runtime informationsys.version
pathlibFilesystem pathsPath("app")
jsonJSON encoding and decodingjson.loads()
loggingApplication logginglogging.getLogger()
datetimeDates and timestampsdatetime.now(UTC)
decimalPrecise decimal arithmeticDecimal("19.99")
uuidUUID generationuuid.uuid4()
enumFixed value setsclass OrderStatus(StrEnum)
reRegular expressionsIdentifier format validation
hashlibCryptographic hashesContent fingerprints
secretsSecure random tokensReset tokens
hmacMessage authenticationSignature verification
base64Binary-to-text encodingEncoding payloads
contextlibContext manager utilitiesResource management
functoolsFunction utilitieslru_cache
collectionsSpecialized collectionsdefaultdict, deque
itertoolsIterator utilitiesEfficient batching
subprocessLaunch external processesRunning system commands
shutilFile operationsCopying and archiving
tempfileTemporary filesTemporary export generation
csvCSV parsing and writingBulk product import
asyncioAsynchronous task schedulingAsync application primitives
concurrent.futuresThread and process poolsBlocking work offloading
unittest.mockMock objectsIsolated 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.

FeatureWhat it doesExample
APIRouterOrganizes endpoints into modulesproducts_router
Path parametersCapture values from URL paths/products/{product_id}
Query parametersFilter or configure requests?limit=20&offset=0
Request bodiesReceive JSON or other payloadsProductCreate
Pydantic modelsValidate and serialize dataProductResponse
Dependency injectionSupply reusable dependenciesDatabase session
Depends()Declare dependenciesDepends(get_db_session)
Security()Declare security-related dependencies and scopesOAuth2 scopes
Response modelsDefine and filter response dataresponse_model=ProductResponse
Status codesReturn correct HTTP semantics201, 404, 409
Exception handlersStandardize error responsesapp.add_exception_handler()
MiddlewareApply cross-cutting request/response behaviorRequest IDs, CORS
LifespanManage startup and shutdown resourcesInitialize clients and dispose engines
Background tasksRun small post-response tasksNon-critical notifications
File uploadsAccept uploaded filesUploadFile
Streaming responsesStream response dataLarge exports
WebSocketsMaintain bidirectional connectionsLive updates
OpenAPIGenerate API schema and docs/openapi.json
Tags and metadataOrganize API documentationProducts, Inventory
OAuth2 supportIntegrate OAuth2 flowsBearer token authentication
Request and ResponseAccess HTTP request and response detailsHeaders, cookies
HTTPExceptionRaise HTTP errorsHTTPException(404, ...)
TestClientTest endpointsUsed 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:

  • BaseModel for request and response schemas
  • Field() for constraints and metadata
  • field_validator for field-level validation
  • model_validator for cross-field validation
  • ConfigDict for model configuration
  • model_validate() for validating Python objects
  • model_dump() for serializing models
  • model_dump(exclude_unset=True) for partial updates
  • model_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

PackagePurposeWhen to use
httpxHTTP client supporting async requestsExternal catalog APIs, payment providers
requestsSynchronous HTTP clientScripts or synchronous applications
tenacityConfigurable retriesTransient external-service failures
websocketsWebSocket client/server primitivesSpecialized WebSocket integrations
orjsonFast JSON serializationWhen benchmarks justify it
python-multipartMultipart form/file parsingFastAPI file uploads
email-validatorEmail address validationEmail 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

PackagePurpose
pwdlibPassword hashing integrations; commonly used with Argon2
PyJWTCreate and verify JSON Web Tokens
cryptographyCryptographic primitives and protocol support
authlibOAuth and OpenID Connect integrations
slowapiRate limiting integration for FastAPI/Starlette
itsdangerousSigned 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

PackagePurpose
pytestTest runner and assertion framework
pytest-asyncioAsync test support
httpxHTTP client used by many FastAPI tests
coverage / pytest-covMeasure test coverage
ruffLinting and formatting
mypy or pyrightStatic type checking
pre-commitRun checks before commits
factory-boyTest data factories
hypothesisProperty-based testing
respxMock 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

PackagePurpose
structlogStructured logging
python-json-loggerJSON-formatted standard logging
sentry-sdkError and performance monitoring
opentelemetry-apiInstrumentation interfaces
opentelemetry-sdkTelemetry processing
OpenTelemetry exporters/instrumentationsSend traces and metrics to observability systems
prometheus-clientExpose 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/packagePurposeWhen to use
redisAsync Redis client supportCaching, counters, coordination
CeleryDistributed task processingReliable background jobs
ARQAsync Redis-based task queueAsync-oriented background work
dramatiqBackground task processingDistributed job workloads
aio-pikaAsync AMQP/RabbitMQ clientMessage-driven services
aiokafkaAsync Kafka clientEvent 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

PackagePurpose
boto3 / aioboto3AWS SDK and async-compatible workflows
PillowImage processing
openpyxlExcel workbook processing
pandasTabular data analysis and imports
pypdfPDF reading and manipulation
python-magicFile type identification where supported
Cloud-provider SDKsObject 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.

SettingDevelopmentProduction
DEBUGUsually enabledDisabled
DatabaseLocal/dev databaseManaged or production database
LoggingHuman-readable, detailedStructured, operationally useful
Secret valuesLocal untracked environment fileSecret manager or protected deployment environment
CORSLocal frontend originsExplicit approved origins
Database SQL echoSometimes usefulUsually disabled
Error responsesConsistent API errorsNever 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.

AreaPackage or toolPriority
API frameworkfastapiNow
ASGI serveruvicorn[standard]Now
Validation and settingspydantic, pydantic-settingsNow
ORM and async databasesqlalchemy[asyncio], asyncpgNow
Database migrationsalembicNow
Environment configurationEnvironment variables and local .envNow
LoggingStandard-library loggingNow
HTTP integrationshttpxWhen external API integration begins
Authenticationpwdlib, PyJWT or an appropriate session/token solutionWhen auth is implemented
Linting and formattingruffEarly in development
Testingpytest, pytest-asyncio, httpxDedicated learning phase
CacheredisWhen caching is justified
Background processingCelery, ARQ, or another chosen queueWhen asynchronous jobs are needed
ObservabilityOpenTelemetry and/or an error-monitoring serviceBefore production at an appropriate level
DeploymentDocker and CI/CD toolingBefore 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:

  1. Configuration and application lifecycle: pydantic-settings, environment variables, lifespan, resource cleanup.
  2. FastAPI request processing: routers, path/query/body parameters, Pydantic schemas, dependency injection, response models.
  3. Database management: SQLAlchemy sessions, transactions, connection pools, relationships, and Alembic migrations.
  4. Error handling and logging: application exceptions, HTTP mappings, safe error responses, useful logs.
  5. Security: authentication, authorization, token lifecycle, tenant isolation, secrets, and CORS.
  6. External integrations: HTTPX, timeouts, retries, idempotency, provider adapters, and failure handling.
  7. Production operations: Docker, CI/CD, health checks, monitoring, caching, and background jobs.
  8. Testing: after understanding the core features, study pytest and apply it throughout the application.