- Python FastAPI Starter
A starter template for building FastAPI applications with Poetry, Docker, semantic-release, lefthook, and VS Code integration.
src/python_fastapi_starter/cli.py— CLI utilities and entry pointssrc/python_fastapi_starter/api/— FastAPI app code (main.py, routers, models, etc.)tests/— Pytest test filesdb/schema.sql— Database schemadb/seed.sql— Seed data.github/workflows/— CI/CD workflowsDockerfile&docker-compose.yaml— Containerizationpyproject.toml— Poetry dependencies & scriptsruff.toml— Ruff linting & formatting configurationlefthook.yml— Git hooks configurationscripts/— Project automation scripts (see below)
All project automation and hook scripts should be placed in the scripts/ directory. Current scripts include:
pre_push.sh— Used by Lefthook for pre-push checks (linting, tests, etc.)postinstall.sh— Post-install setup steps topython-autoenv.sh— Used by direnv for automated Python environment setup
If you add custom automation or hook scripts, place them in this directory for consistency.
-
Clone the repo:
git clone <repo-url> cd python-fastapistarter
-
Automated Python virtualenv setup (recommended):
There is an
.envrcfile included in the repo, so no setup of thevirtualenvis needed except approving it i.e.direnv allow. The automated environment uses direnv, the.envrcand the includedscripts/python-autoenv.shscript:-
Install direnv (if not already installed):
- On Ubuntu/Debian:
sudo apt install direnv
- On macOS (Homebrew):
brew install direnv
- Or see direnv installation docs
- On Ubuntu/Debian:
-
Enable direnv in your shell:
- For bash, add to
~/.bashrc:eval "$(direnv hook bash)"
- For zsh, add to
~/.zshrc:eval "$(direnv hook zsh)"
- For bash, add to
-
Approve the environment:
direnv allow
This enables the automated setup: when you enter the project directory, direnv will create and activate
.venvif missing and install dependencies automatically.
If you don't use direnv, you can remove the
.envrcfile and manually set up the environment, but I don't recommend that, automation prevents stupid mistakes:python3 -m venv .venv source .venv/bin/activate poetry install -
-
Set up environment variables:
- For local development, create a
.env.localfile if needed (e.g., for DB connection, secrets). - For GitHub releases, set
GH_TOKENin your GitHub repository secrets.
- For local development, create a
Environment variables are used to configure the application.
.env: Used for static/build-time environment variables. These are values that do not change across deployment environments (e.g.,ROOT_PATH). This file is copied into the Docker image and is available in all environments..env.local: Used for secrets and environment-specific variables (e.g., credentials, API keys, local overrides). This file should NOT be copied into the Docker image and is intended for local development or runtime overrides. It is referenced viaenv_fileindocker-compose.ymland can be injected at runtime in production (e.g., via Kubernetes secrets).
Best practice:
- Keep sensitive values (secrets, credentials) in
.env.localand out of version control. - Use
.envfor configuration that is safe to bake into the image and share across environments. - In Docker Compose, you can reference both files in
env_fileto allow local overrides and secrets to take precedence over static values.
docker compose up --build- API: http://localhost:8000
- DB: PostgreSQL/PostGIS, data persisted in Docker volume
poetry run start- Hot-reloading enabled
- Use the provided launch and task configurations in
.vscode/. - Press
F5or run tasks forLint & Format,Test, andRun API,Start Docker.
poetry run testpoetry run test tests/test_main.pypoetry run test tests/test_main.py::test_read_rootYou can pass additional arguments to Poetry scripts defined in pyproject.toml. For example:
- Run a specific test file:
poetry run test tests/test_main.py - Run a single test:
poetry run test tests/test_main.py::test_read_root - Lint and format a specific directory:
poetry run lint src/python_fastapi_starter/api
Arguments after the script name are forwarded to the underlying tool (pytest, ruff, etc.).
Ruff is used for both linting and formatting. Configuration is in ruff.toml. It should be the default provider for python in VS Code, see .vscode/settings.json.
Import Sorting: Ruff is configured to sort imports automatically as part of the linting process. Running poetry run lint will fix lint issues, format code, and sort imports—no separate step is required.
NOTE: There is an issue with Ruff in that it doesn't automatically format import statements unless you run the lint command. So while it may fix code on save/past/type, it won't update the imports. Make sure to run poetry run lint or Lint & Format task regularly to keep imports sorted.
- To lint and format code manually, use the Poetry scripts:
This runs both linting, formatting, and import sorting on
poetry run lint
srcandtests. - Or use the VS Code task: Lint & Format.
- Releases are automated via GitHub Actions (
.github/workflows/release.yml). - Ensure your commit messages follow the Angular commit guidelines.
- Set
GH_TOKENin your GitHub repository secrets for release automation. - To trigger a release manually:
- Go to GitHub Actions → RELEASE → Run workflow
- Managed by
lefthook(seelefthook.yml). - Pre-push hook runs lint and tests via
scripts/pre_push.sh.
- On the first run, schema and seed data are loaded from
db/schema.sqlanddb/seed.sql. - Data is persisted in Docker volume
db_data.
To completely reset the database and start from scratch:
- Stop all running containers:
docker compose down
- Remove the database volume:
(If your project folder name changes, adjust the volume name accordingly.)
docker volume rm python-fastapistarter_db_data
- Start containers again:
This will re-run the schema and seed scripts and create a fresh database.
docker compose up --build
You can debug your FastAPI app using either Docker or VS Code, depending on your workflow preference.
- For a richer experience, use the VS Code debugger with the provided launch configuration (
.vscode/launch.json).- This uses
debugpyautomatically and starts the app viacli.py. - Press
F5or select "Run FastAPI (cli.py with Docker DB)" in the Run & Debug panel. - The database will be started automatically before the app launches.
- This uses
If you prefer to run the app manually in your terminal (with Docker running for the database), you can debug using Python's built-in debugger:
- Add a breakpoint in your code:
import pdb; pdb.set_trace()
- Start the database using Docker Compose:
docker compose up -d
- Run the app in your terminal:
poetry run start
- When execution reaches the breakpoint, you'll drop into the interactive pdb debugger in your terminal.
This method works for any local development workflow and does not require extra dependencies. For more advanced debugging (e.g., remote attach), see the VS Code section above.
- To use Python's built-in debugger (
pdb), run your container interactively:Or adddocker compose run --service-ports --rm <service> python -m pdb <your_script.py>
import pdb; pdb.set_trace()in your code and start the service normally:This will drop you into the debugger in your terminal when the breakpoint is hit.docker compose run --service-ports --rm <service> poetry run start
- Customize
.gitmessagefor commit templates - Use VS Code recommended extensions for best experience
- All dependencies are managed in
pyproject.tomlfor reproducibility
We welcome contributions! Please follow these guidelines:
- Code style: Use Ruff for linting and formatting. Run
poetry run lintbefore submitting PRs. - Testing: Ensure all tests pass with
poetry run test. - Commit messages: Follow the Angular commit guidelines. A commit message template is provided in
.gitmessage. - PRs: Make pull requests against the
mainbranch. Include a clear description and reference any related issues. - Release management: Releases are automated via semantic-release and GitHub Actions. See the release section above for details.
When deploying behind NGINX (e.g., in Kubernetes), add the following to your NGINX config for improved security:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location / {
limit_req zone=api_limit burst=20 nodelay;
# ...other config...
}
}
These headers and rate limiting settings help protect your API from common web vulnerabilities and basic DoS attacks. Adjust values as needed for your environment.