FastAPI FOUNDATION

How to Structure Your FastAPI Projects

Part 1: Blueprint

Amir Lavasani
5 min readMay 14, 2024

In this article, we will discuss two main strategies for structuring your FastAPI projects and highlight the scenarios in which each one is more suitable.

Why Proper Structuring Matters?

Here are the most important reasons to structure your code based on best practices.

  1. Enhanced Scalability: Well-organized code grows smoothly with your FastAPI project.
  2. Improved Maintainability: Easy-to-understand code makes updates and fixes simple.
  3. Streamlined Collaboration: Clear structure helps teams work together efficiently.
Dall-E generated image with the following concept: An abstract software blueprint

Key Principles for Structuring Projects

In building FastAPI applications, it’s crucial to follow these best practices:

  1. Separation of Concerns: Separate different aspects of your FastAPI project, such as routes, models, and business logic, for clarity and maintainability.
  2. Modularization: Break down your FastAPI application into reusable modules to promote code reusability and organization.
  3. Dependency Injection: Decouple components by implementing dependency injection, which allows for more flexible and testable code in FastAPI using libraries like fastapi.Dependencies.
  4. Testability: Prioritize writing testable code in FastAPI applications by employing strategies like dependency injection and mocking to ensure robust testing and quality assurance.

Structuring Formats

FastAPI applications can be structured in different ways to accommodate various project needs.

There are two main approaches for structuring projects. One is based on file type and the other is based on module functionality.

1. Structuring based on File-Type

In this approach, files are organized by type (e.g., API, CRUD, models, schemas, routers) as represented by FastAPI itself.

This structure is more suitable for microservices or projects with fewer scopes:

.
β”œβ”€β”€ app # Contains the main application files.
β”‚ β”œβ”€β”€ __init__.py # this file makes "app" a "Python package"
β”‚ β”œβ”€β”€ main.py # Initializes the FastAPI application.
β”‚ β”œβ”€β”€ dependencies.py # Defines dependencies used by the routers
β”‚ β”œβ”€β”€ routers
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ items.py # Defines routes and endpoints related to items.
β”‚ β”‚ └── users.py # Defines routes and endpoints related to users.
β”‚ β”œβ”€β”€ crud
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ item.py # Defines CRUD operations for items.
β”‚ β”‚ └── user.py # Defines CRUD operations for users.
β”‚ β”œβ”€β”€ schemas
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ item.py # Defines schemas for items.
β”‚ β”‚ └── user.py # Defines schemas for users.
β”‚ β”œβ”€β”€ models
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ item.py # Defines database models for items.
β”‚ β”‚ └── user.py # Defines database models for users.
β”‚ β”œβ”€β”€ external_services
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ email.py # Defines functions for sending emails.
β”‚ β”‚ └── notification.py # Defines functions for sending notifications
β”‚ └── utils
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ authentication.py # Defines functions for authentication.
β”‚ └── validation.py # Defines functions for validation.
β”œβ”€β”€ tests
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ test_main.py
β”‚ β”œβ”€β”€ test_items.py # Tests for the items module.
β”‚ └── test_users.py # Tests for the users module.
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ .gitignore
└── README.md

In this structure

  • app/: Contains the main application files.
  • main.py: Initializes the FastAPI application.
  • dependencies.py: Defines dependencies used by the routers.
  • routers/: Contains router modules.
  • crud/: Contains CRUD (Create, Read, Update, Delete) operation modules.
  • schemas/: Contains Pydantic schema modules.
  • models/: Contains database model modules.
  • external_services/: Contains modules for interacting with external services.
  • utils/: Contains utility modules.
  • tests/: Contains test modules.

2. Structuring based on Module-Functionality

In the second approach, we organize our files based on package functionality for example authentication sub-package, users sub-package, and posts sub-package.

The module-functionality structure is better suited for monolithic projects containing numerous domains and modules. By grouping all file types required by a single sub-package, it improves development efficiency.

This structure is suggested by the fastapi-best-practices GitHub repository.

In this structure, Each package has its own router, schemas, models, etc.

fastapi-project
β”œβ”€β”€ alembic/
β”œβ”€β”€ src
β”‚ β”œβ”€β”€ auth
β”‚ β”‚ β”œβ”€β”€ router.py # auth main router with all the endpoints
β”‚ β”‚ β”œβ”€β”€ schemas.py # pydantic models
β”‚ β”‚ β”œβ”€β”€ models.py # database models
β”‚ β”‚ β”œβ”€β”€ dependencies.py # router dependencies
β”‚ β”‚ β”œβ”€β”€ config.py # local configs
β”‚ β”‚ β”œβ”€β”€ constants.py # module-specific constants
β”‚ β”‚ β”œβ”€β”€ exceptions.py # module-specific errors
β”‚ β”‚ β”œβ”€β”€ service.py # module-specific business logic
β”‚ β”‚ └── utils.py # any other non-business logic functions
β”‚ β”œβ”€β”€ aws
β”‚ β”‚ β”œβ”€β”€ client.py # client model for external service communication
β”‚ β”‚ β”œβ”€β”€ schemas.py
β”‚ β”‚ β”œβ”€β”€ config.py
β”‚ β”‚ β”œβ”€β”€ constants.py
β”‚ β”‚ β”œβ”€β”€ exceptions.py
β”‚ β”‚ └── utils.py
β”‚ └── posts
β”‚ β”‚ β”œβ”€β”€ router.py
β”‚ β”‚ β”œβ”€β”€ schemas.py
β”‚ β”‚ β”œβ”€β”€ models.py
β”‚ β”‚ β”œβ”€β”€ dependencies.py
β”‚ β”‚ β”œβ”€β”€ constants.py
β”‚ β”‚ β”œβ”€β”€ exceptions.py
β”‚ β”‚ β”œβ”€β”€ service.py
β”‚ β”‚ └── utils.py
β”‚ β”œβ”€β”€ config.py # global configs
β”‚ β”œβ”€β”€ models.py # global database models
β”‚ β”œβ”€β”€ exceptions.py # global exceptions
β”‚ β”œβ”€β”€ pagination.py # global module e.g. pagination
β”‚ β”œβ”€β”€ database.py # db connection related stuff
β”‚ └── main.py
β”œβ”€β”€ tests/
β”‚ β”œβ”€β”€ auth
β”‚ β”œβ”€β”€ aws
β”‚ └── posts
β”œβ”€β”€ templates/
β”‚ └── index.html
β”œβ”€β”€ requirements
β”‚ β”œβ”€β”€ base.txt
β”‚ β”œβ”€β”€ dev.txt
β”‚ └── prod.txt
β”œβ”€β”€ .env
β”œβ”€β”€ .gitignore
β”œβ”€β”€ logging.ini
└── alembic.ini

In this structure

Store all domain directories inside src folder.

  1. src/: The highest level of an app, contains common models, configs, and constants, etc.
  2. src/main.py: Root of the project, which inits the FastAPI app

Each package has its own router, schemas, models, etc.

  1. router.py: is the core of each module with all the endpoints
  2. schemas.py: for pydantic models
  3. models.py: for database models
  4. service.py: module-specific business logic
  5. dependencies.py: router dependencies
  6. constants.py: module-specific constants and error codes
  7. config.py: e.g. env vars
  8. utils.py: non-business logic functions, e.g. response normalization, data enrichment, etc.
  9. exceptions.py: module-specific exceptions, e.g. PostNotFound, InvalidUserData

When a package requires services or dependencies or constants from other packages, import them with an explicit module name to avoid ambiguity.

from src.auth import constants as auth_constants
from src.notifications import service as notification_service
from src.posts.constants import ErrorCode as PostsErrorCode # in case we have Standard ErrorCode in constants module of each package

Finally

In conclusion, structuring your FastAPI projects is essential for maintaining scalability, readability, and maintainability as your application grows. By organizing your code effectively, you ensure that it remains manageable and adaptable to evolving requirements.

We discussed two main types of structuring FastAPI projects: the file-type structure and the module-functionality structure.

The file-type structure, represented by FastAPI itself, organizes files based on their type (e.g., routers, schemas, models). This structure is suitable for microservice architectures where individual services have single responsibilities.

On the other hand, the module-functionality structure separates files based on module functionality rather than file type. This approach is more suitable for larger monolithic applications, promoting better organization and maintainability.

In choosing the right structure for your FastAPI project, consider factors such as project size, complexity, and architectural design. Ultimately, whether you opt for the file-type structure or the module functionality structure, prioritizing organization and clarity will contribute to the long-term success of your FastAPI applications.

Resources

--

--

Amir Lavasani

I delve into machine learning πŸ€– and software architecture 🏰 to enhance my expertise while sharing insights with Medium readers. πŸ“ƒ