ہر FastAPI پروجیکٹ ایک ہی طرح شروع ہوتا ہے: ایک main.py فائل، چند routes، ایک دو Pydantic models، اور handler کے اندر ہی inline database call۔ یہ کام کرتا ہے۔ اسے لکھنا تیز ہوتا ہے، پڑھنا تیز ہوتا ہے، اور پہلے چند ہفتوں تک ایسا لگتا ہے جیسے structure پر زیادہ سوچنے کی ضرورت ہی نہیں۔ پھر پروجیکٹ بڑھتا ہے — مزید routes، مزید models، ایک background job، چھ endpoints میں مشترک auth dependency — اور وہ ایک فائل 2,000 لائنوں کی ایسی scroll بن جاتی ہے جہاں کچھ بھی ڈھونڈنے کے لیے function name grep کرنا پڑتا ہے اور امید رکھنی پڑتی ہے کہ درست چیز مل جائے۔
غلطی یہ نہیں کہ شروع میں flat main.py لکھ دی جائے۔ غلطی یہ ہے کہ اس لمحے کے لیے کوئی منصوبہ نہ ہو جب وہ structure کام کرنا بند کر دے۔ زیادہ تر teams یا تو بہت دیر تک انتظار کرتی ہیں اور پھر گھبراہٹ میں big-bang rewrite کرتی ہیں، یا پہلے ہی دن overcorrect کر دیتی ہیں اور Java enterprise app سے کاپی کیا ہوا folder structure اپنا لیتی ہیں جو صرف تین endpoints والے پروجیکٹ میں بھی indirection کی پانچ تہیں ڈال دیتا ہے۔ ان دونوں انتہاؤں میں سے کوئی بھی چیز growth کے ساتھ نہیں چلتی۔ جو چیز واقعی چلتی ہے وہ ایسا structure ہے جسے آپ سادہ انداز میں شروع کریں اور سوچ سمجھ کر evolve کریں — ایسا structure جو آپ کو مجبور ہونے سے پہلے concerns الگ کرنے دے، اور circular import errors سے بھی بچا لے جو عین اس وقت سامنے آتے ہیں جب codebase اتنا بڑا ہو چکا ہوتا ہے کہ اسے سلجھانا آسان نہیں رہتا۔
آپ یہ سیکھیں گے:
- flat
main.pystructure حقیقت میں کب ٹوٹنا شروع ہوتا ہے، اور وہ واضح نشانیاں جن پر نظر رکھنی چاہیے - layered (technical) layout اور domain-driven (feature-based) layout میں اصل فرق کیا ہے، اور کون سا کس team کے لیے زیادہ مناسب ہے
APIRouterکے ساتھ routers کو compose کیسے کریں تاکہ route registration خود maintenance کا بوجھ نہ بن جائے- FastAPI projects میں circular imports خاص طور پر کیوں آتے ہیں، اور import direction کے وہ اصول جو انہیں ہونے سے روکتے ہیں
- ایک مکمل، واضح رائے پر مبنی folder structure جسے آپ سیدھا نئے پروجیکٹ میں استعمال کر سکتے ہیں
- موجودہ flat app کو full rewrite کے بغیر scalable structure میں کیسے migrate کریں
فہرستِ مضامین
- بنیادی باتیں
- Layered بمقابلہ Domain-Driven Layouts
- APIRouter کے ساتھ Router Composition
- FastAPI ایپس میں Circular Imports کیوں ہوتے ہیں
- ایک ایسا Structure جو Scale کرے
- مشترک کوڈ: Core، Schemas، اور Dependencies
- موجودہ Flat App کو Migrate کرنا
- عام غلطیاں
- Production Best Practices
- یہاں سے آگے کہاں جائیں
بنیادی باتیں
وہ نشانیاں کہ Flat Structure ٹوٹ رہی ہے
ایک single-file FastAPI app چھوٹے پیمانے پر غلط نہیں ہوتا — مسئلے کے سائز کے حساب سے یہی درست مقدار کا structure ہوتا ہے۔ تبدیلی کا اشارہ لائنوں کی تعداد نہیں بلکہ friction ہوتا ہے: آپ main.py کھولتے ہیں تاکہ ایک endpoint شامل کریں، مگر وہاں پہنچنے کے لیے پندرہ غیر متعلقہ endpoints کے اوپر سے scroll کرنا پڑتا ہے؛ ایک ہی فائل میں دو لوگ تبدیلی کریں تو تقریباً ہر PR پر merge conflicts بنتے ہیں؛ تین routes میں استعمال ہونے والا Pydantic model import کرنے کے بجائے copy-paste ہونے لگتا ہے کیونکہ کسی کو جلدی سے یہ سمجھ نہیں آتا کہ اس کی “اصل” version کہاں موجود ہے۔
یہ سب abstract معنی میں architecture failures نہیں ہیں — یہ navigation failures ہیں۔ اس کا حل کوئی نیا framework نہیں بلکہ متعلقہ code کو متعلقہ code کے قریب رکھنا اور imports کو ایسی direction دینا ہے جو خود اپنے اوپر واپس loop نہ کرے۔
FastAPI کو خاص طور پر منصوبے کی ضرورت کیوں ہے
FastAPI، Django کی طرح apps کے ذریعے project structure impose نہیں کرتا، اور یہی flexibility ایک دو دھاری تلوار ہے۔ اس کا مطلب یہ ہے کہ آپ اپنی team کے مطابق structure بنا سکتے ہیں، لیکن اس کا یہ بھی مطلب ہے کہ کوئی guardrail موجود نہیں جو routers folder کو services سے import کرنے سے روکے، اور پھر services دوبارہ کسی helper کے reuse کے لیے routers سے import کر لے — ایک ایسی غلطی جس تک Django کی app boundaries پہلے دن اتنی آسانی سے پہنچنے نہیں دیتیں۔ ایک بڑی FastAPI application دراصل ایک بڑی Python application ہی ہوتی ہے جس پر web layer چڑھی ہوتی ہے، اس لیے اسے Python packaging کے تمام مسائل inherit ہوتے ہیں، اور ساتھ ہی FastAPI کا اپنا dependency injection system بھی، جس کی اپنی structural آراء ہوتی ہیں کہ Depends callables کہاں رہنے چاہییں۔
Layered بمقابلہ Domain-Driven Layouts
بڑھتے ہوئے FastAPI codebase کو منظم کرنے کے دو غالب طریقے ہیں، اور درست انتخاب project size سے کم اور اس بات سے زیادہ جڑا ہے کہ آپ کی team روزمرہ میں واقعی کام کیسے کرتی ہے۔
Layered (Technical) Structure
Layered structure فائلوں کو اس بنیاد پر گروپ کرتا ہے کہ وہ ہیں کیا — تمام routers ایک ساتھ، تمام schemas ایک ساتھ، تمام database models ایک ساتھ:
app/
├── routers/
│ ├── users.py
│ ├── orders.py
│ └── payments.py
├── schemas/
│ ├── users.py
│ ├── orders.py
│ └── payments.py
├── models/
│ ├── users.py
│ ├── orders.py
│ └── payments.py
├── services/
│ ├── users.py
│ ├── orders.py
│ └── payments.py
└── main.py
یہ وہ structure ہے جو زیادہ تر FastAPI tutorials سکھاتی ہیں، اور یہ چھوٹے سے درمیانے درجے کے APIs کے لیے اچھا کام کرتا ہے، خاص طور پر جب ایک یا دو engineers زیادہ تر codebase کو چھوتے ہوں۔ اس کا نقصان app کے بڑھنے پر سامنے آتا ہے: ایک feature شامل کرنا — مثلاً refunds capability — ایک ہی functionality کے لیے چار یا پانچ مختلف top-level folders میں تبدیلی مانگتا ہے، اور آسانی سے ایسا ہو جاتا ہے کہ schema تو شامل ہو جائے مگر اس کا matching router نہ ہو، یا router خاموشی سے service layer کو چھوڑ کر سیدھا database سے بات کرنے لگے۔
Domain-Driven (Feature-Based) Structure
Domain-driven structure فائلوں کو اس بنیاد پر گروپ کرتا ہے کہ وہ business کے لیے کیا کرتی ہیں — orders سے متعلق ہر چیز ایک ساتھ رہتی ہے، چاہے وہ router ہو، schema ہو، یا service:
app/
├── users/
│ ├── router.py
│ ├── schemas.py
│ ├── models.py
│ ├── service.py
│ └── dependencies.py
├── orders/
│ ├── router.py
│ ├── schemas.py
│ ├── models.py
│ ├── service.py
│ └── dependencies.py
├── payments/
│ ├── router.py
│ ├── schemas.py
│ ├── models.py
│ ├── service.py
│ └── dependencies.py
└── main.py
یہ بڑے teams اور بڑے domains کے لیے بہتر scale کرتا ہے، کیونکہ ہر folder ایک self-contained unit کے قریب ہوتا ہے — orders پر کام کرنے والے نئے engineer کو شاذونادر ہی payments کھولنے کی ضرورت پڑتی ہے، اور یہ folder code ownership کے لیے ایک قدرتی boundary بن جاتا ہے۔ اس کا tradeoff یہ ہے کہ واقعی چھوٹی ایپس کے لیے شروع میں کچھ زیادہ ceremony آ جاتی ہے، اور کبھی کبھی دو domains کو بہت ملتے جلتے schemas درکار ہوں تو کچھ duplication بھی ہوتا ہے۔
سادہ اصول: اگر آپ کے پاس تقریباً دس سے کم routes ہیں اور ایک یا دو contributors ہیں تو layered سے شروع کریں۔ اس کے بعد، یا جیسے ہی ایک ہی feature مستقل طور پر چار غیر متعلقہ folders میں پھیلنے لگے، domain-driven پر منتقل ہو جائیں۔ Netflix کی public engineering write-ups اور زیادہ تر large-scale FastAPI references اسی وجہ سے domain-driven کی طرف جاتی ہیں — یہی وہ layout ہے جو headcount بڑھنے کے ساتھ merge conflicts اور teams کے ایک دوسرے کے کام میں مداخلت کو کم رکھتا ہے، چاہے پہلے دن اس میں structure کی لاگت کچھ زیادہ ہو۔
APIRouter کے ساتھ Router Composition
آپ کوئی بھی layout چنیں، routes کو آپس میں wire کرنے کا mechanism ایک ہی ہے: APIRouter۔ ہر feature module اپنا router خود define کرتا ہے بجائے اس کے کہ routes کو براہِ راست global FastAPI() app پر register کرے:
# app/orders/router.py
from fastapi import APIRouter, Depends
from app.orders.schemas import OrderOut, OrderCreate
from app.orders.service import create_order, get_order
from app.core.dependencies import get_current_user
router = APIRouter(prefix="/orders", tags=["orders"])
@router.post("/", response_model=OrderOut)
async def create_order_endpoint(
payload: OrderCreate,
user=Depends(get_current_user),
):
return await create_order(payload, user)
@router.get("/{order_id}", response_model=OrderOut)
async def read_order(order_id: int, user=Depends(get_current_user)):
return await get_order(order_id, user)
پھر ایک ہی جگہ — عموماً app/api.py یا app/main.py — تمام feature routers کو app میں compose کرتی ہے:
# app/api.py
from fastapi import APIRouter
from app.users.router import router as users_router
from app.orders.router import router as orders_router
from app.payments.router import router as payments_router
api_router = APIRouter()
api_router.include_router(users_router)
api_router.include_router(orders_router)
api_router.include_router(payments_router)
# app/main.py
from fastapi import FastAPI
from app.api import api_router
app = FastAPI(title="My API")
app.include_router(api_router, prefix="/api/v1")
اس سے آپ کو تین چیزیں ملتی ہیں جو flat app میں خودبخود نہیں ملتیں: ایک ہی جگہ جہاں آپ ہر registered route group دیکھ سکتے ہیں، API کو version کرنے کی قدرتی جگہ (/api/v1, /api/v2 کو الگ APIRouter trees کے طور پر)، اور ہر router کے لیے tags اور prefix تاکہ OpenAPI docs ہر route پر boilerplate ڈالے بغیر منظم رہیں۔ یہی وہ structural foundation بھی ہے جس پر اس پورے cluster کی باقی بحث کھڑی ہے — آپ یہاں router کے ساتھ جو dependencies attach کرتے ہیں، انہی پر ہم FastAPI Dependency Injection: Patterns and Anti-Patterns میں تفصیل سے بات کرتے ہیں۔
FastAPI ایپس میں Circular Imports کیوں ہوتے ہیں
Circular imports بڑھتی ہوئی FastAPI apps میں سب سے عام structural bug ہیں، اور ان کا pattern خاصا قابلِ پیشگوئی ہوتا ہے: ایک router کسی service کو import کرتا ہے، service کسی schema کو import کرتی ہے، اور — چونکہ کسی نے validation helper reuse کرنا ہوتا ہے — schema دوبارہ router module سے کچھ import کر لیتی ہے۔ Python ImportError: cannot import name 'X' from partially initialized module اٹھا دیتا ہے اور fix ظاہری طور پر اصل وجہ سے غیر متعلق لگتی ہے۔
اصل وجہ تقریباً ہمیشہ ایک missing direction rule ہوتی ہے۔ ایک اچھی طرح structured FastAPI app میں imports صرف ایک ہی سمت میں بہنے چاہییں:
router → service → models/schemas → core
Router، service کو import کر سکتا ہے۔ Service، models اور schemas کو import کر سکتی ہے۔ models، schemas، یا core میں موجود کوئی چیز کبھی بھی router یا service سے import نہیں کرنی چاہیے — dependency graph میں وہ اوپر ہیں، نیچے نہیں۔ جب یہ rule ٹوٹتی ہے تو عموماً وجہ یہ ہوتی ہے کہ schema کو کوئی ایسا type چاہیے ہوتا ہے جو router کے قریب define کیا گیا ہو، یا دو domains کو کوئی helper مشترک چاہیے ہوتا ہے اور کوئی شخص اس مشترک چیز کو core میں promote کرنے کے بجائے domains کے درمیان سیدھا import کر لیتا ہے۔
تین واضح fixes تقریباً ہر حقیقی case کو cover کرتی ہیں:
- واقعی مشترک code کو
coreیا کسی dedicatedsharedpackage میں promote کریں۔ اگرordersاورpaymentsدونوں کوMoneytype چاہیے، تو وہ کسی ایک domain میں نہیں ہونا چاہیے — اسےapp/core/types.pyمیں ہونا چاہیے، اور دونوں domains اسے نیچے کی سمت import کریں۔ - صرف type-only cross-references کے لیے
TYPE_CHECKINGاستعمال کریں۔ اگر schema کو کسی دوسرے module سے type hint صرف annotations کے لیے چاہیے، تو import کو guard کر دیں تاکہ runtime پر execute نہ ہو:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from app.orders.models import Order
def summarize(order: "Order") -> str:
...
- آخری حل کے طور پر function کے اندر local import سے cycle توڑ دیں — یہ کام تو کرتا ہے، لیکن یہ اس بات کی علامت ہے کہ module boundary غلط ہے، نہ کہ کوئی ایسا pattern جسے default طور پر اختیار کیا جائے۔
یہاں layered بمقابلہ domain-driven کا فیصلہ بھی اہم ہے: domain-driven layout cross-domain imports کو بصری طور پر واضح بنا دیتا ہے (app/orders/ کے اندر from app.payments.service import x)، اس لیے یہ notice کرنا — اور سوال اٹھانا — کہیں آسان ہو جاتا ہے کہ domain boundary عبور کی جا رہی ہے، بہ نسبت layered structure کے جہاں ہر چیز پہلے ہی shared top-level folders میں رہتی ہے۔
ایک ایسا Structure جو Scale کرے
درمیانے سے بڑے FastAPI application کے لیے، عملی طور پر یہ ایسا structure ہے جو اچھا ثابت ہوتا ہے: ایک domain-driven core کے ساتھ ایک چھوٹا layered core package جو واقعی cross-cutting concerns کو سنبھالتا ہے:
app/
├── main.py # creates the FastAPI() instance, mounts api_router
├── api.py # top-level APIRouter composing all domain routers
├── config.py # Settings via pydantic-settings, loaded once
├── core/
│ ├── database.py # engine, session factory
│ ├── security.py # JWT/password hashing helpers
│ ├── dependencies.py # get_current_user, get_db, shared Depends
│ ├── exceptions.py # custom exception classes + handlers
│ └── logging.py
├── users/
│ ├── router.py
│ ├── schemas.py
│ ├── models.py
│ ├── service.py
│ └── dependencies.py
├── orders/
│ ├── router.py
│ ├── schemas.py
│ ├── models.py
│ ├── service.py
│ └── dependencies.py
└── tests/
├── users/
└── orders/
ہر domain folder self-contained اور isolation میں import ہونے کے قابل ہوتا ہے — آپ orders/ کو مکمل طور پر delete بھی کر دیں تو users/ پھر بھی کام کرے گا۔ core ہر domain کے نیچے بیٹھتا ہے اور کبھی کسی domain سے import نہیں کرتا، اور یہی چیز پورے graph کو acyclic بناتی ہے۔ یہی وہ layout ہے جس کا حوالہ اس cluster کے باقی حصوں میں بھی آتا ہے: core/dependencies.py میں موجود get_db dependency وہ جگہ ہے جہاں Async SQLAlchemy Sessions in FastAPI, Done Right میں زیرِ بحث sessions wire ہوتے ہیں، اور core/dependencies.py ہی وہ جگہ ہے جہاں آپ shared auth dependency رکھیں گے بجائے اس کے کہ اسے ہر router میں الگ الگ duplicate کریں۔
مشترک کوڈ: Core، Schemas، اور Dependencies
core کے لیے ایک خاص rule ہونا چاہیے: یہ ایسے code کے لیے ہے جس کی کسی ایک domain کے بارے میں خاص رائے نہ ہو۔ Database session factory کو اس سے کوئی فرق نہیں پڑتا کہ وہ orders serve کر رہی ہے یا users — اس لیے وہ core میں ہونی چاہیے۔ get_current_user dependency ہر جگہ استعمال ہوتی ہے مگر ایک ہی بار implement ہوتی ہے — اس لیے وہ core میں ہونی چاہیے۔ اس کے برعکس OrderStatus جیسی چیز لیں، ایک ایسا enum جو orders domain کے باہر بے معنی ہے — وہ orders/models.py میں ہی رہنا چاہیے، core میں نہیں، چاہے متعدد functions اس کا حوالہ دے رہے ہوں اور وہ بظاہر “shared” محسوس ہو۔
ایک عام anti-pattern یہ ہے کہ schemas.py بڑھتے بڑھتے app کے ہر Pydantic model کا dumping ground بن جاتی ہے، حتیٰ کہ ان models کا بھی جو اصل میں domain-specific ہوتے ہیں۔ اگر کوئی schema صرف ایک domain کے router اور service میں استعمال ہو رہا ہے، تو وہ اسی domain کے folder کے اندر ہونا چاہیے — اسے global schemas/ package میں منتقل کرنے سے وہ زیادہ reusable نہیں بنتا، بس اسے ڈھونڈنا مشکل ہو جاتا ہے۔
# app/core/dependencies.py
from fastapi import Depends, HTTPException, status
from app.core.database import get_db
from app.core.security import decode_token
async def get_current_user(token: str = Depends(decode_token), db=Depends(get_db)):
user = await db.get_user_by_token(token)
if user is None:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid credentials")
return user
ہر domain router، get_current_user کو core سے import کرتا ہے، کبھی کسی اور domain کی dependencies.py سے نہیں — یہی یک طرفہ flow import graph کو web کے بجائے tree بنائے رکھتا ہے۔
موجودہ Flat App کو Migrate کرنا
کام کرنے والی flat main.py کو ایک ہی pull request میں مکمل domain-driven structure میں rewrite کرنا regressions لانے اور migration کو آدھے راستے میں روک دینے کا آسان طریقہ ہے۔ اس سے زیادہ محفوظ راستہ یہ ہے:
- سب سے پہلے
app/core/بنائیں اور database session، config، اور shared auth dependency کو اس میں منتقل کریں — ابھی کچھ اور نہ بدلیں، اور app چلتا رہے۔ - سب سے مصروف domain منتخب کریں (عام طور پر وہ جس میں سب سے زیادہ routes ہوں یا حالیہ bugs سب سے زیادہ ہوں) اور اسے اپنے الگ folder میں extract کریں — router، schemas، models، service — اور ساتھ ساتھ imports بھی update کرتے جائیں۔
main.pyکو update کریں تاکہ وہ نئے extract کیے گئے domain کو، ابھی تک flat پڑے باقی حصے کے ساتھ،include_routerکرے۔- ہر domain کے لیے یہی عمل دہرائیں، ایک وقت میں ایک PR، تاکہ ہر تبدیلی reviewable ہو اور ہر step کے بعد app deploy ہونے کے قابل رہے۔
- Flat file سب سے آخر میں delete کریں، جب ہر route domain folder میں منتقل ہو چکا ہو اور
main.pyصرف چیزوں کو wire کر رہی ہو۔
یہی طریقہ زیادہ تر حقیقی teams اپناتی ہیں — incremental انداز میں، working tests کی حفاظت کے ساتھ، نہ کہ big-bang rewrite کے طور پر۔
عام غلطیاں
غلطی: routers کا دوسرے routers سے import کرنا۔ تقریباً ہمیشہ اس کا مطلب یہ ہوتا ہے کہ کوئی shared dependency duplicate ہوئی ہے، core میں promote نہیں کی گئی۔ حل: اگر دو routers کو ایک ہی چیز چاہیے، تو وہ core میں ہونی چاہیے، کسی ایک router میں نہیں۔
غلطی: business logic کا route handlers کے اندر رہنا۔ ایسا handler جو database query بھی کرے، business rules بھی apply کرے، اور response format بھی کرے، logic کو پورا HTTP layer چلائے بغیر test نہ ہونے کے قابل بنا دیتا ہے۔ حل: handlers کو ہلکا رکھیں — input parse کریں، service function کو call کریں، result واپس کریں — اور logic کو service.py میں رکھیں جہاں اسے براہِ راست unit test کیا جا سکے۔
غلطی: پوری app کے لیے ایک ہی بہت بڑی schemas.py۔ اس سے واضح نہیں رہتا کہ کون سے schemas کہاں واقعی استعمال ہو رہے ہیں، اور refactors ایسی فائل کو چھیڑتی ہیں جس میں باقی سب بھی edit کر رہے ہوتے ہیں۔ حل: schemas کو اسی domain کے دائرے میں رکھیں جس کی ownership ان کے پاس ہے۔
غلطی: layered اور domain-driven structure کو غیر مستقل انداز میں ملانا۔ آدھی app domain کے حساب سے منظم ہو، آدھی technical layer کے حساب سے، اور نئے code کے لیے کوئی واضح rule نہ ہو۔ حل: جان بوجھ کر ایک pattern منتخب کریں اور اسے document کریں، چاہے README میں صرف ایک جملہ ہی کیوں نہ ہو۔
غلطی: تین-endpoint prototype کو پانچ layers کے ساتھ over-engineer کرنا۔ Repository interfaces، abstract service classes، اور dependency-injection containers ایسی app کے لیے جس میں صرف تین routes ہوں، ابھی فائدے کے بغیر لاگت بڑھاتے ہیں۔ حل: structure کو حقیقی size کے مطابق رکھیں — layered یا حتیٰ کہ flat انداز سے شروع کریں، اور domain-driven کی طرف اس وقت بڑھیں جب درد واضح ہو، اس سے پہلے نہیں۔
Production Best Practices
- یک طرفہ import rule نافذ کریں۔
router → service → models/schemas → core، اس کے الٹ کبھی نہیں۔ اگر آپ اسے automate کرنا چاہتے ہیں توimport-linterCI میں module boundaries enforce کر سکتا ہے۔ - API کو router-composition layer پر version کریں، نہ کہ ہر endpoint پر —
v1کے ساتھ mount کیا گیاv2APIRoutertree handlers کے اندر version checks بکھیرنے کی نسبت کہیں کم error-prone ہوتا ہے۔ main.pyکو boring رکھیں۔ اسے app بنانی چاہیے، routers mount کرنے چاہییں، اور exception handlers اور lifespan events register کرنے چاہییں — اس کے علاوہ کچھ نہیں۔- ہر domain کے لیے ایک
tests/<domain>/folder لکھیں، app structure کی نقل کرتے ہوئے، تاکہ فوراً واضح ہو کہ نیا test کہاں ہونا چاہیے۔ - حقیقی inflection points پر structure کا دوبارہ جائزہ لیں — جیسے نیا domain، دوسری team، یا service extraction — نہ کہ کسی مقررہ schedule پر۔
یہاں سے آگے کہاں جائیں
Project structure وہ بنیاد ہے جس پر FastAPI application کا باقی behavior کھڑا ہوتا ہے، مگر یہ اکیلے ہر مسئلہ حل نہیں کرتا۔ جب folders اپنی جگہ آ جائیں تو اگلے مسائل عموماً runtime سے متعلق ہوتے ہیں — اور یہ cluster ان پانچ اہم مسائل کا احاطہ کرتا ہے جو سب سے زیادہ سامنے آتے ہیں:
- آپ کا FastAPI Endpoint Event Loop کو کیوں Block کرتا ہے —
defبمقابلہasync defاور ان blocking calls کی تشخیص جو ہر دوسری request کو روک دیتی ہیں - FastAPI BackgroundTasks بمقابلہ Celery: درست انتخاب کیسے کریں — background work کہاں ہونی چاہیے، اس کے لیے فیصلہ سازی کا فریم ورک
- FastAPI Dependency Injection: Patterns and Anti-Patterns —
Depends، yield-based teardown، اور dependency caching - Async SQLAlchemy Sessions in FastAPI, Done Right — session scoping،
MissingGreenleterrors، اور lifespan-managed engines - FastAPI میں Streaming: SSE بمقابلہ WebSockets بمقابلہ Polling — token-by-token اور real-time responses کے لیے transport کا انتخاب
خلاصہ
FastAPI کے لیے کوئی ایک واحد درست folder structure نہیں ہے — بلکہ درست structure وہ ہے جو آپ کی team کے سائز اور آپ کے domain کی شکل کے مطابق ہو، مستقل انداز میں لاگو کیا گیا ہو، اور imports کس سمت بہیں گے اس کے بارے میں واضح rule رکھتا ہو۔ سادہ انداز میں شروع کریں، اس مخصوص friction پر نظر رکھیں جو تبدیلی کا اشارہ دیتی ہے، اور جب تبدیلی کریں تو working tests کے پیچھے ایک وقت میں ایک domain منتقل کریں، rewrite کی کوشش نہ کریں۔ مقصد کبھی folder names نہیں تھے — مقصد ایسا codebase تھا جہاں چالیسواں endpoint شامل کرنا اتنا ہی آسان ہو جتنا چوتھا شامل کرنا تھا۔
کیا آپ کی موجودہ FastAPI app ابھی بھی ایک ہی فائل پر مشتمل ہے، یا وہ اپنی موجودہ structure سے پہلے ہی بڑی ہو چکی ہے؟
