Skip to content

noahhefner/fastapi-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FastAPI Project Template

This repository presents a flexible, scalable way to structure a RESTful FastAPI application.

Overview

REST APIs often expose endpoints for multiple business domains. A domain is a functional area of the API (in this repository, the two example domains are items and orders).

In many FastAPI templates, the repository separates code by "type" into four primary areas:

  • Routers: Handling HTTP requests and mapping business logic errors to HTTP status codes.
  • Models/Schemas: Validating request and response data shapes.
  • Services: Business logic.
  • Tests: Unit and integration tests.

The result is a repository structured something like this:

my_fastapi_project/
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── dependencies.py
│   ├── routers/
│   │   ├── __init__.py
│   │   ├── users.py
│   │   └── items.py
│   ├── internal/
│   │   ├── __init__.py
│   │   └── admin.py
│   ├── core/
│   │   ├── __init__.py
│   │   ├── config.py
│   │   └── security.py
│   ├── models/
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── item.py
│   ├── schemas/
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── item.py
│   ├── services/
│   │   ├── __init__.py
│   │   ├── user_service.py
│   │   └── item_service.py
│   └── db/
│       ├── __init__.py
│       ├── database.py
│       └── migrations/
├── tests/
│   ├── __init__.py
│   ├── test_main.py
│   ├── test_users.py
│   ├── test_items.py
├── .env
├── .gitignore
├── requirements.txt
├── README.md
└── run.sh

While this works for small projects, as the API grows, developers often have to keep track of multiple parallel directory structures to work on a single feature.

This repository argues for a different approach: domains serve as the primary organizational unit. All logic for a given domain is encapsulated within its own directory:

├── domains
│   ├── __init__.py
│   ├── items
│   │   ├── get_all_items
│   │   │   ├── __init__.py
│   │   │   └── test_get_all_items.py
│   │   ├── get_item_by_id
│   │   │   ├── __init__.py
│   │   │   └── test_get_item_by_id.py
│   │   └── __init__.py
│   └── orders
│       ├── get_all_orders
│       │   ├── __init__.py
│       │   └── test_get_all_orders.py
│       ├── get_order_by_id
│       │   ├── __init__.py
│       │   ├── errors.py
│       │   ├── get_order_by_id.py
│       │   ├── models.py
│       │   └── test_get_order_by_id.py
│       └── __init__.py

By encapsulating each endpoint in its own directory, you gain two primary benefits:

  1. Colocated Related Code: By placing data models, business logic, unit tests, and HTTP handling in the same directory, developers no longer need to jump between distant folders to make a single functional change.
  2. Flexible Complexity: Not every endpoint is complex. Some might simply execute a short SQL query and return a response (ex. items endpoints). Others might involve dynamic query building, third-party APIs, or complex authorization. This structure allows simple endpoints to exist in a single file, while complex ones can be broken down into separate files for models, business logic, and errors within the same directory (ex. get_order_by_id).

When To Use This Structure (And When Not To)

I created this template repository because I was frustrated with the poor developer ergonomics of the FastAPI “best-practice” layouts found online, especially in larger codebases. This layout provides a practical balance between developer experience and organizational rigidity.

If you are building a quick proof-of-concept or a single-domain API, this structure may be overkill. However, if your API has many domains, is expected to scale, or you simply prefer that related code stay grouped together, this structure is an excellent choice.

Testing

Pytest is used for testing the API endpoints. Following the same philosophy as the rest of the repository, test case files are colocated with the code they cover.

A shared conftest.py overrides the get_db dependency, ensuring each test runs against a fresh database instance.

To run the tests:

uv run pytest

Running the HTTP Server

Start the server using the following command:

uv run fastapi dev src/main.py

Docker

A Dockerfile is provided to demonstrate how to containerize this application.

# -----------------------
# Stage 1: Build
# -----------------------
FROM ghcr.io/astral-sh/uv:python3.14-alpine AS builder

# Set work directory
WORKDIR /app

# Create virtual environment
RUN uv venv

# Copy pyproject.toml and uv.lock
COPY pyproject.toml uv.lock .

# Install Python modules
RUN uv sync --no-cache --no-group dev

# -----------------------
# Stage 2: Runtime
# -----------------------
FROM python:3.14-alpine3.23 AS runner

# Set work directory
WORKDIR /app

# Copy venv from builder stage
COPY --from=builder /app/.venv /app/.venv

# Copy application code
COPY src .

# Create a non-root user
RUN addgroup -S demo && adduser -S demo -G demo

# Give the non-root user permission to run the script
RUN chown demo:demo /app/main.py && \
    chmod u+rwx /app/main.py

    # Ensure venv is used
ENV PATH="/app/.venv/bin:$PATH"

# Switch to non-root user
USER demo

# Run the program
CMD ["python", "main.py"]

About

Template Repository for FastAPI REST APIs

Resources

Stars

Watchers

Forks

Contributors

Languages