This is a template project for a FastAPI application with a PostgreSQL database, pgAdmin for database management, and Traefik as a reverse proxy. All services are containerized using Docker.
- FastAPI Backend: High-performance API framework
- PostgreSQL Database: Robust relational database
- Alembic Migrations: Database versioning and migrations
- Docker & Docker Compose: Containerization for consistent environments
- pgAdmin: Database management UI
- Traefik: API Gateway and reverse proxy
- Authentication:
- JWT Token-based authentication (Bearer tokens)
- Cookie-based authentication with HTTP-only cookies
- Refresh token functionality
- Password reset functionality
- Environment-based configuration: Different settings for development, staging, and production
-
Local Development:
- Python 3.11 or higher
- PostgreSQL installed locally or accessible
- pip or another Python package manager
-
Docker Deployment:
- Docker and Docker Compose installed
- A code editor (e.g., VS Code)
- A terminal or command prompt
-
Environment Variables: This project uses a
.envfile for local development configuration. If it doesn't exist, run this command to create:cp .env.example .env
Ensure you set the following environment variables in your
.envfile:
# Core settings
FRONTEND_URL='http://localhost:3000'
SECRET_KEY='your_32_char_strong_secret_key_here'
DEBUG=True
ENVIRONMENT='development' # Options: development, staging, production
# Database settings
POSTGRES_USER="your_username"
POSTGRES_PASSWORD="your_password"
POSTGRES_DB_NAME="fastapi_db"
# Authentication
ALGORITHM='HS256'
ACCESS_TOKEN_EXPIRE_MINUTES=30 # Short-lived access tokens for security
REFRESH_TOKEN_EXPIRE_DAYS=7 # Longer-lived refresh tokens-
Install dependencies directly with uv:
# uv will make .venv automatically uv sync -
Install new dependencies with uv:
uv add dependency_name
-
Run server:
uv run uvicorn app.main:app --reload
OR if you have make available
make start
-
Create a virtual environment:
# Using standard venv python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate # OR using uv (faster) uv venv source .venv/bin/activate # On Windows: .venv\Scripts\activate
-
Install dependencies:
# Using pip pip install -r requirements.txt # OR using uv (faster) uv pip install -r requirements.txt
Create your PostgreSQL database and run migrations:
# Make sure you've set the correct DATABASE_URL in your .env file
alembic upgrade headOR if you have make available
make alembic-upgrade
This project uses Alembic for managing database schema migrations. Understanding the migration workflow is essential for all developers working on this project.
-
Migration Tracking:
- Alembic maintains a version table (
alembic_version) in your database - This table stores the current revision ID that has been applied
- Each migration has a unique revision ID (e.g.,
9ead825737b6)
- Alembic maintains a version table (
-
Creating New Migrations:
When you modify SQLAlchemy models (e.g., add/change/remove columns), you need to create and apply migrations:
# Generate a migration automatically by detecting model changes uv run alembic revision --autogenerate -m "Add new user table" # or with make make alembic-revision MSG="Add new user table" # Review the generated migration file in app/alembic/versions/ # Make any necessary adjustments (e.g., adding default values for non-nullable columns) # Apply the migration uv run alembic upgrade head
-
Common Migration Tasks:
# View current migration status uv run alembic current # View migration history uv run alembic history # Downgrade to a specific version
uv run alembic downgrade <revision_id>
uv run alembic downgrade -1
4. **Important Considerations:**
- **Adding Non-Nullable Columns:** When adding non-nullable columns to existing tables, you MUST provide a server_default value
- **JSONB Columns:** Require the PostgreSQL dialect to be properly imported and used
- **Data Migrations:** For complex data transformations, you may need to write custom Python in your migration scripts
- **Testing Migrations:** Always test migrations in a development environment before applying to production
5. **Troubleshooting Migration Issues:**
- If a migration fails, check the error message carefully - common issues include constraint violations or missing dependencies
- If you need to reset a failed migration, you may need to modify the `alembic_version` table directly
- When working with existing data, consider data integrity and constraints
4. **Run the application**:
```bash
uv run uvicorn app.main:app --reload
The API will be available at http://localhost:8000
To build and start all the services (FastAPI application, PostgreSQL database, pgAdmin, and Traefik):
docker compose up --build -d--build: Forces Docker to rebuild the images if there are changes (e.g., in yourDockerfileor application code).-d: Runs the containers in detached mode (in the background).
To stop the services:
docker compose downTo stop and remove volumes (useful for a clean restart, will delete database data):
docker compose down -vTo view logs for all services:
docker compose logs -fTo view logs for a specific service (e.g., fastapi):
docker compose logs -f fastapiOnce the containers are running:
-
Backend API (FastAPI):
- Via Traefik:
http://api.localhost - Directly (if Traefik is not used or for direct port access):
http://localhost:8000 - API Docs (Swagger UI):
http://api.localhost/docsorhttp://localhost:8000/docs - Alternative API Docs (ReDoc):
http://api.localhost/redocorhttp://localhost:8000/redoc
- Via Traefik:
-
pgAdmin (Database Management):
- Via Traefik:
http://pgadmin.localhost - Directly:
http://localhost:9000 - Login Credentials (defined in
docker-compose.yml):- Email:
admin@admin.com - Password:
admin
- Email:
- Via Traefik:
-
Traefik Dashboard (for inspecting routes and services):
http://localhost:8080
This application supports two authentication methods:
- Login: Send a POST request to
/api/auth/tokenwith username/password to get an access token - Usage: Include the token in the Authorization header:
Authorization: Bearer <your_token> - API Docs: Works with Swagger UI's "Authorize" button
- Login: Send a POST request to
/api/auth/loginwith username/password - Security: Tokens are stored in HTTP-only cookies for protection against XSS attacks
- Refresh: When the access token expires, use
/api/auth/refreshto get a new one - Logout: Send a POST request to
/api/auth/logoutto clear authentication cookies
Cookie security settings are automatically configured based on the ENVIRONMENT variable:
- Development: Less restrictive settings (HTTP allowed, lax same-site policy)
- Staging: HTTPS required, lax same-site policy
- Production: HTTPS required, strict same-site policy
After logging into pgAdmin, you'll need to register your PostgreSQL server (the db service from docker-compose.yml):
- In the pgAdmin browser tree (left panel), right-click on Servers.
- Select Register -> Server....
- In the General tab:
- Name: Enter a descriptive name for your server (e.g.,
Local Docker DB,fastapi_db_service).
- Name: Enter a descriptive name for your server (e.g.,
- Switch to the Connection tab:
- Host name/address:
db(This is the service name of your PostgreSQL container indocker-compose.yml). - Port:
5432(Default PostgreSQL port). - Maintenance database:
fastapi_db(This is thePOSTGRES_DBvalue from yourdbservice environment). - Username:
texagon(This is thePOSTGRES_USERvalue). - Password:
password(This is thePOSTGRES_PASSWORDvalue). - You can leave other settings as default or adjust as needed.
- Host name/address:
- Click Save.
Your database server should now appear in the list, and you can browse its contents, run queries, etc.
#service.py
from app.utils.pagination import paginator
@paginator(UserSchema)
async def get_users_service(
db: Session,
current_user: CurrentUser,
start_date: date = None,
end_date: date = None,
page: int = 1,
page_size: int = 10,
):
query = db.query(UserModel)
if start_date:
query = query.filter(UserModel.created_at >= start_date)
if end_date:
query = query.filter(UserModel.created_at <= end_date)
return query
#router.py
from .service import users_service
from app.schema.pagination import PaginationResponseSchema
@router.get(
"/users",
response_model=PaginationResponseSchema[UserSchema],
status_code=status.HTTP_200_OK,
)
async def get_users(
request: Request,
current_user: CurrentUser,
db_session: DbSession,
start_date: date = None,
end_date: date = None,
page: int = 1,
per_page: int = 10,
):
users = await get_users_service(
db_session, current_user, start_date, end_date, page, per_page
)
return usersThis project uses pre-commit to automatically check and format code before each commit. Pre-commit hooks help enforce code style, catch errors early, and keep your codebase clean.
uv run pre-commit install
uv run pre-commit autoupdate
uv run pre-commit run --all-files
Code formatting: black, autopep8, isort
Linting: flake8, bandit, autoflake
YAML and whitespace checks
Security checks: bandit
If you add new hooks or update .pre-commit-config.yaml, run:
uv run pre-commit install
uv run pre-commit autoupdate
To clear the pre-commit cache (if you see strange errors):
uv run pre-commit clean
This repo includes Make targets to run linting, formatting, and start the dev server. You can use the Makefile targets or run the underlying commands directly with uv.
- Run linter (ruff):
make lint
# or
uv run ruff check ./app- Format code (ruff):
make format
# or
uv run ruff format ./app- Start development server (uvicorn with auto-reload):
make start
# or
uv run uvicorn app.main:app --reloadNotes:
uvwill create/manage the virtual environment for you if you use it.- If you don't have
makeavailable, use theuv run ...commands shown above. - Run
pre-commit run --all-filesto apply all pre-commit checks and auto-fixes before committing.
from app.utils.cache import cache_response, invalidate_cache_key
@router.get("/items")
@cache_response(ttl=15, key="items") #ttl is time to live in seconds
async def get_items():
items = await fetch_items_from_db() # Replace with actual DB fetch logic
return items
# invalidate cache for items
@router.post("/add-items")
async def add_item(item):
item = await add_item_to_db(item) # Replace with actual DB insert logic
# Invalidate the cache for items
await invalidate_cache_key("items")
return {"message": "Items added to cache"}.
app/
├── features/
│ └── auth/
│ ├── router.py # REST API endpoints
│ ├── service.py # Business logic
│ ├── schema.py # Pydantic models
│ └── graphql/
│ ├── types/ # GraphQL types
│ ├── queries/ # GraphQL queries
│ └── mutations/ # GraphQL mutations
├── models/
│ ├── user.py # SQLAlchemy models
│ └── deal.py # Deal model
├── utils/
│ ├── dependencies.py # Common dependencies
│ ├── security.py # Password & JWT utilities
│ ├── permissions.py # Role-based access control
│ ├── database.py # Database configuration
│ ├── config.py # Application settings
│ ├── api.py # Regiseter api routes
│ └── exception_handler.py # Custom error handlers
├── graphql/
│ ├── schema.py # Main GraphQL schema
│ └── context.py # GraphQL context
├── alembic/ # Database migrations
├── main.py # FastAPI application
├── alembic/ # Alembic database migration scripts
├── tests/ # Unit and integration tests
├── .env # Local environment variables (create this file)
├── .gitignore
├── alembic.ini # Alembic configuration
├── docker-compose.yml # Docker Compose configuration
├── Dockerfile # Dockerfile for the FastAPI application
├── entrypoint.sh # Entrypoint script for the FastAPI container
├── pyproject.toml # Project metadata and dependencies (using Poetry/uv)
├── README.md # This file
└── uv.lock # Lock file for dependencies managed by uv
-
Database Connection Errors:
- Check if PostgreSQL is running
- Verify your DATABASE_URL in the .env file
- Ensure your PostgreSQL user has proper permissions
-
Authentication Issues:
- Make sure SECRET_KEY is set correctly
- Check that COOKIE_SECURE is False in development if not using HTTPS
-
Alembic Migration Errors:
- Run
alembic revision --autogenerate -m "message"to create a new migration - Check your database models for any issues
- Run
If you encounter issues not covered here, please check the FastAPI documentation or create an issue in the repository.
Happy coding!