پروژه FastAPI معمولاً خیلی تمیز شروع میشه؛ یه فایل main.py دارید، چند مسیر API داخلش تعریف میکنید و همه‌چیز کاملاً قابل کنترل به نظر میرسه. مشکل زمانی خودش رو نشون میده که بخش ورود کاربران، محصولات، سفارش‌ها، پنل مدیریت و چند قابلیت دیگه به پروژه اضافه میشن و فایل اصلی کم‌کم به صدها یا حتی هزاران خط میرسه. اون موقع پیدا کردن یه مسیر ساده سخت‌تر میشه، چند نفر نمی‌تونن راحت روی بخش‌های مختلف کار کنن و هر تغییر کوچیکی احتمال درگیر کردن قسمت‌های نامرتبط پروژه رو بالا میبره.

FastAPI برای جلوگیری از همین وضعیت ابزاری برای تقسیم مسیرهای API به بخش‌های مستقل داره که اسمش APIRouter هست. با استفاده از اون میشه مسیرهای مربوط به کاربران رو داخل یه فایل، محصولات رو داخل فایل دیگه و بخش مدیریت رو هم جدا نگه داشت، در حالی که همه اون‌ها همچنان بخشی از یک برنامه FastAPI باقی میمونن. خود مستندات رسمی FastAPI هم برای برنامه‌های بزرگ‌تر، تقسیم پروژه به چند فایل و استفاده از APIRouter رو به عنوان روش اصلی سازمان‌دهی مسیرها توضیح میدن.

مشکل نوشتن همه Endpointها داخل main.py چیست؟

خود main.py جای بدی برای تعریف چند مسیر ساده نیست؛ اتفاقاً برای پروژه آموزشی یا یه API خیلی کوچک کاملاً مناسبه. مشکل از جایی شروع میشه که فایل اصلی هم وظیفه ساخت برنامه رو داشته باشه، هم مسیرهای کاربران رو مدیریت کنه، هم محصولات رو بشناسه، هم Authentication داخلش باشه و هم تنظیمات بخش مدیریت و سرویس‌های دیگه بهش اضافه بشن.

در چنین شرایطی فایل اصلی دیگه نقش نقطه شروع برنامه رو نداره و تبدیل میشه به انبار کل پروژه. برای پیدا کردن یه مسیر مربوط به سفارش باید بین کدهای User و Product بگردید و تغییر یه بخش هم ممکنه باعث Conflict بیشتر بین اعضای تیم بشه. بهتره main.py بیشتر مسئول ساخت برنامه و متصل کردن بخش‌های مختلف باشه، نه اینکه تمام منطق مسیرهای API داخل خودش قرار بگیره.

APIRouter در FastAPI چیست؟

تصور کنید برای هر بخش پروژه یه مجموعه مسیر مستقل میسازیم و در نهایت همه این مجموعه‌ها رو به برنامه اصلی وصل میکنیم. ابزاری که FastAPI برای ساخت چنین مجموعه‌ای ارائه میده APIRouter نام داره.

رفتارش از نظر تعریف مسیر خیلی شبیه خود FastAPI() هست؛ یعنی به جای اینکه مسیر رو با @app.get() یا @app.post() تعریف کنید، داخل فایل مربوط به همون بخش از @router.get() و @router.post() استفاده میکنید. بعد در main.py این Router با include_router() به برنامه اصلی اضافه میشه. FastAPI تمام مسیرهای اون Router رو وارد برنامه اصلی و مستندات خودکار API میکنه.

ساختار ساده یک پروژه FastAPI با APIRouter

مثلاً پروژه‌ای دارید که فعلاً فقط کاربران و محصولات رو مدیریت میکنه. به جای قرار دادن همه مسیرها داخل main.py میشه پوشه‌ای برای Routerها ساخت و هر بخش رو داخل فایل خودش نگه داشت.

نمونه ساده:

# app/routers/users.py
from fastapi import APIRouter

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


@router.get("/")
async def get_users():
    return [{"id": 1, "username": "amir"}]


@router.get("/{user_id}")
async def get_user(user_id: int):
    return {"id": user_id}


# app/routers/products.py
from fastapi import APIRouter

router = APIRouter(
    prefix="/products",
    tags=["products"]
)


@router.get("/")
async def get_products():
    return [{"id": 1, "name": "Laptop"}]


# app/main.py
from fastapi import FastAPI
from app.routers import users, products

app = FastAPI()

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


@app.get("/")
async def root():
    return {"message": "API is running"}

اینجا مسیرهای User و Product کاملاً از هم جدا هستن، ولی وقتی برنامه اجرا میشه همه اون‌ها بخشی از همون FastAPI اصلی هستن. بنابراین /users/ و /products/ درست مثل زمانی کار میکنن که مستقیماً داخل main.py تعریف شده باشن.

prefix چه مشکلی را حل می‌کند؟

وقتی چند مسیر مربوط به یه بخش دارید، تکرار کردن قسمت اول آدرس روی همه اون‌ها منطقی نیست. مثلاً اگه ده مسیر مربوط به User داشته باشید، نوشتن /users داخل تک‌تک Routeها فقط کد رو تکراری میکنه.

برای تعریف قسمت مشترک ابتدای همه آدرس‌های یک Router از گزینه‌ای به اسم prefix استفاده میشه. وقتی prefix="/users" تعریف شده، مسیری که داخل Router فقط "/{user_id}" نوشته شده در برنامه نهایی به شکل /users/{user_id} در دسترس قرار میگیره.

این موضوع وقتی API نسخه‌بندی میشه هم مفیده؛ مثلاً میشه Routerها رو هنگام اتصال به برنامه اصلی زیر مسیرهایی مثل /api/v1 قرار داد. FastAPI حتی اجازه میده یه Router با Prefixهای مختلف بیش از یک بار به برنامه اضافه بشه، هرچند این قابلیت بیشتر برای سناریوهای خاص کاربرد داره.

tags چه کاربردی دارد؟

FastAPI خودش صفحه مستندات تعاملی API تولید میکنه و وقتی تعداد Endpointها زیاد بشه، اون صفحه هم میتونه شلوغ بشه. برای گروه‌بندی مسیرهای مرتبط داخل مستندات API میشه از tags استفاده کرد.

مثلاً تمام مسیرهای Router کاربران با Tag مربوط به Users نمایش داده میشن و مسیرهای محصولات داخل گروه خودشون قرار میگیرن. این گزینه روی منطق اجرای Endpoint تأثیری نداره، ولی مستندات /docs رو خیلی مرتب‌تر میکنه و پیدا کردن مسیرها برای Front-end یا اعضای دیگه تیم راحت‌تر میشه.

main.py بعد از استفاده از Router چه نقشی دارد؟

وقتی پروژه درست تقسیم شده باشه، main.py معمولاً خیلی ساده‌تر میشه. برنامه اصلی ساخته میشه، Middlewareها و تنظیمات عمومی اضافه میشن و Routerهای مختلف به اون متصل میشن. در واقع main.py بیشتر شبیه مرکز اتصال قطعات پروژه عمل میکنه.

این ساختار باعث میشه وقتی دنبال مسیر مربوط به کاربران هستید مستقیم برید سراغ routers/users.py و برای تغییر بخش محصولات وارد products.py بشید. لازم نیست یه فایل چند هزار خطی رو جستجو کنید تا بفهمید کد موردنظر کجاست.

آیا برای هر Endpoint باید یک فایل جدا بسازیم؟

نه؛ این هم میتونه پروژه رو بیش از حد خرد کنه. هدف APIRouter تقسیم منطقی پروژه‌ست، نه اینکه هر تابع رو داخل یه فایل جدا قرار بدیم.

معمولاً بهتره مسیرهایی که از نظر موضوعی به هم مرتبط هستن کنار هم قرار بگیرن؛ مثلاً Login و Register میتونن داخل بخش Auth باشن، عملیات کاربران داخل Users و مسیرهای مربوط به محصولات داخل Products. وقتی یکی از این فایل‌ها خودش خیلی بزرگ شد، اون موقع میشه ساختار رو یه مرحله بیشتر تقسیم کرد.

ساختار خوب باید پیدا کردن کد رو ساده‌تر کنه، نه اینکه برای پیدا کردن یه تابع مجبور بشید بین ۵۰ فایل کوچک جابه‌جا بشید.

APIRouter فقط برای مرتب کردن فایل‌ها نیست

مزیت Router فقط این نیست که main.py کوتاه‌تر بشه. FastAPI اجازه میده تنظیماتی مثل Prefix، Tag، وابستگی‌های مشترک و حتی بعضی پاسخ‌های مشخص رو روی کل Router اعمال کنید. بنابراین اگه تمام مسیرهای بخش مدیریت باید یه بررسی امنیتی مشترک داشته باشن، میشه اون وابستگی رو روی Router اعمال کرد و از تکرار تنظیمات روی تک‌تک Endpointها جلوگیری کرد. مستندات FastAPI هم نمونه اتصال Router با Prefix، Tag و Dependency مشترک رو ارائه میکنن.

این ویژگی وقتی پروژه بزرگ میشه اهمیت بیشتری پیدا میکنه، چون هر بخش میتونه قوانین و تنظیمات خودش رو داشته باشه بدون اینکه فایل اصلی پر از جزئیات بشه.

چه زمانی استفاده از APIRouter ارزش دارد؟

اگه پروژه فقط دو یا سه Endpoint آزمایشی داره، تقسیم کردن اون به چند پوشه و فایل ممکنه بیشتر از اینکه کمک کنه ساختار اضافه ایجاد کنه. اما همین که پروژه چند بخش مشخص مثل User، Auth، Product یا Order پیدا میکنه، معمولاً زمان خوبیه که مسیرها رو از main.py جدا کنید.

بهتره این کار قبل از تبدیل شدن فایل اصلی به یه فایل عظیم انجام بشه. مرتب کردن یه پروژه ۳۰۰ خطی خیلی راحت‌تر از پروژه‌ایه که چند هزار خط کد توی main.py جمع کرده و بخش‌های مختلفش به هم وابسته شدن.

اشتباه رایج؛ انتقال کد بدون تقسیم مسئولیت

گاهی برای مرتب کردن پروژه فقط چند فایل میسازیم ولی تمام منطق همچنان قاطی میمونه؛ مثلاً فایل users.py علاوه بر مسیرهای User شامل Queryهای پیچیده، ارسال ایمیل، ساخت Token و کلی منطق تجاری هم هست. این کار فقط مشکل یه فایل بزرگ رو به چند فایل بزرگ تبدیل میکنه.

Router بهتره مسئول مسیرهای API و هدایت Request به بخش مناسب باشه و وقتی منطق پروژه پیچیده‌تر میشه، کارهای اصلی رو میشه به Serviceها، Dependencyها یا لایه‌های مناسب دیگه منتقل کرد. APIRouter شروع خوبی برای ساختاردهیه، ولی به تنهایی معماری کل پروژه رو حل نمیکنه.

جمع‌بندی

APIRouter در FastAPI راه ساده‌ای برای تقسیم Endpointهای یه پروژه به بخش‌های مستقل و قابل مدیریت فراهم میکنه. به جای اینکه User، Product، Auth و Admin همگی داخل main.py نوشته بشن، میشه برای هر بخش Router جدا ساخت و در نهایت با include_router() اون‌ها رو به برنامه اصلی متصل کرد.

استفاده از prefix مسیر مشترک هر بخش رو مشخص میکنه، tags مستندات API رو مرتب‌تر نگه میداره و Routerها میتونن تنظیمات یا Dependencyهای مشترک خودشون رو هم داشته باشن. نتیجه اینه که main.py کوچک‌تر میمونه، پیدا کردن کد راحت‌تر میشه و توسعه پروژه وقتی تعداد Endpointها بالا میره دردسر کمتری خواهد داشت.

قاعده ساده اینه: تا وقتی پروژه کوچیکه سادگی رو حفظ کن، اما وقتی بخش‌های مستقل شکل گرفتن، قبل از اینکه main.py تبدیل به انبار پروژه بشه مسیرها رو جدا کن.