Hackforge Academy

Category: python

Introduction to FastAPI Project Structure

Published on 22 Aug 2026

Explanation

A good FastAPI project structure separates application responsibilities into different modules and folders. Instead of putting all routes, database logic, models, and business logic inside a single main.py file, a structured project keeps each responsibility separate. This makes the application easier to understand, test, maintain, and scale. A typical FastAPI application contains folders for routes, models, schemas, services, database configuration, and tests.

Code:

fastapi-project/
│
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── database.py
│   ├── models.py
│   ├── schemas.py
│   ├── services.py
│   └── routes/
│       ├── __init__.py
│       ├── users.py
│       └── products.py
│
├── tests/
│   ├── __init__.py
│   ├── test_users.py
│   └── test_products.py
│
├── requirements.txt
├── .env
├── .gitignore
└── README.md

Explanation

The main.py file is commonly used as the entry point of a FastAPI application. It creates the FastAPI application object and includes routers from different modules. Keeping route definitions outside main.py prevents the main application file from becoming too large. The application can then be started using Uvicorn. The title and description can also be configured when creating the FastAPI application.

Code:

from fastapi import FastAPI
from app.routes import users, products

app = FastAPI(
    title='Employee Management API',
    description='FastAPI REST API Example',
    version='1.0.0'
)

app.include_router(users.router)
app.include_router(products.router)


@app.get('/')
def root():
    return {'message': 'FastAPI application running'}


# Run:
# uvicorn app.main:app --reload

Explanation

FastAPI provides APIRouter to organize related API endpoints into separate modules. For example, all user-related endpoints can be placed in users.py and all product-related endpoints can be placed in products.py. The router is then registered with the main FastAPI application using include_router(). This approach makes the API modular and easier to maintain as the number of endpoints grows.

Code:

# app/routes/users.py

from fastapi import APIRouter

router = APIRouter(
    prefix='/users',
    tags=['Users']
)


@router.get('/')
def get_users():
    return [
        {'id': 1, 'name': 'John'},
        {'id': 2, 'name': 'Alice'}
    ]


@router.get('/{user_id}')
def get_user(user_id: int):
    return {
        'id': user_id,
        'name': 'John'
    }


# main.py
# app.include_router(users.router)

Explanation

FastAPI applications commonly separate database models, request/response schemas, and business logic. Models represent database tables when using an ORM such as SQLAlchemy. Pydantic schemas define the structure and validation rules for API request and response data. Service modules contain business logic so that route functions remain simple. This separation follows the principle of separation of concerns and makes the application easier to test and extend.

Code:

# app/schemas.py

from pydantic import BaseModel


class UserCreate(BaseModel):
    name: str
    email: str


class UserResponse(BaseModel):
    id: int
    name: str
    email: str


# app/services.py

def create_user(user):
    # Business logic goes here
    return {
        'id': 1,
        'name': user.name,
        'email': user.email
    }

Explanation

Production FastAPI applications usually keep database configuration and environment-specific settings separate from application logic. The database.py module can manage database connections or SQLAlchemy sessions, while environment variables can store sensitive configuration such as database URLs and secret keys. Tests should be placed in a separate tests directory. This structure makes it easier to run automated tests and deploy the same application across development, testing, and production environments.

Code:

# app/database.py

from sqlalchemy import create_engine

DATABASE_URL = 'postgresql://postgres:password@localhost/company'

engine = create_engine(DATABASE_URL)


# .env
# DATABASE_URL=postgresql://postgres:password@localhost/company
# SECRET_KEY=your-secret-key


# tests/test_users.py

def test_user_name():
    user = {
        'id': 1,
        'name': 'John'
    }

    assert user['name'] == 'John'


# requirements.txt
# fastapi
# uvicorn
# sqlalchemy
# psycopg
# pydantic

🚀 Learn Spring Boot with real-world projects

💡 Build REST APIs step by step

🧠 Improve backend development skills

🎯 Get career-ready practical training

Join Our Free WhatsApp Community

Direct access to niche-specific mentors and peers on WhatsApp.

🐍

Python Community

Discuss Django, FastAPI, AI integration, and automation scripts with 15k+ developers.

Join Python Community
⚛️

React Community

Master Next.js, Framer Motion, and State Management. Share your latest UI components.

Join React Community

Java Community

Deep dives into Spring Boot, Microservices architecture, and high-performance backend ops.

Join Java Community