FastAPI FOUNDATION
How to Structure Your FastAPI Projects
Part 1: Blueprint
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.
- Enhanced Scalability: Well-organized code grows smoothly with your FastAPI project.
- Improved Maintainability: Easy-to-understand code makes updates and fixes simple.
- Streamlined Collaboration: Clear structure helps teams work together efficiently.
Key Principles for Structuring Projects
In building FastAPI applications, itβs crucial to follow these best practices:
- Separation of Concerns: Separate different aspects of your FastAPI project, such as routes, models, and business logic, for clarity and maintainability.
- Modularization: Break down your FastAPI application into reusable modules to promote code reusability and organization.
- Dependency Injection: Decouple components by implementing dependency injection, which allows for more flexible and testable code in FastAPI using libraries like
fastapi.Dependencies
. - 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.
src/
: The highest level of an app, contains common models, configs, and constants, etc.src/main.py
: Root of the project, which inits the FastAPI app
Each package has its own router, schemas, models, etc.
router.py
: is the core of each module with all the endpointsschemas.py
: for pydantic modelsmodels.py
: for database modelsservice.py
: module-specific business logicdependencies.py
: router dependenciesconstants.py
: module-specific constants and error codesconfig.py
: e.g. env varsutils.py
: non-business logic functions, e.g. response normalization, data enrichment, etc.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.