FastAPI Complete Guide
Bilkul Zero se Pro Level tak — Step by Step
✓ Beginner Friendly • Hindi-English • 20 ChaptersWhat is an API?
Sabse pehle — API kya hai?
API ka full form hai Application Programming Interface. Sunne mein complicated lagta hai, but actually bahut simple concept hai.
Imagine karo tum ek restaurant mein ho:
- Tum = Customer (Client/Frontend)
- Kitchen = Server (Backend/Database)
- Waiter = API
- Menu = API Documentation
Tum directly kitchen mein nahi jaate. Tum waiter ko bolo (request bhejo), waiter kitchen jaata hai (server), khaana laata hai (response). Waiter hi API hai!
Technically: API ek set of rules hai jo define karta hai ki do software ek doosre se kaise baat karenge.
APIs kyun exist karte hain?
- Security: Tum directly database access nahi kar sakte (kitchen mein ghusna allowed nahi hai!)
- Organization: Sab kuch structured hota hai — pehle order do, phir khaana aayega
- Reusability: Ek hi API ko mobile app, web app, dono use kar sakte hain
- Separation: Frontend alag team banaye, backend alag — API se connect ho jaayenge
Client-Server Concept
- Client = Jo request bhejta hai (browser, mobile app, Postman)
- Server = Jo request receive karta hai, process karta hai, response bhejta hai
- Request = "Mujhe ye data chahiye" ya "Ye data save karo"
- Response = Server ka jawab (data ya error message)
REST API Kya Hai?
REST = REpresentational State Transfer. Ye ek style hai APIs banana ka. Isme rules hain:
- Har cheez ka ek URL hota hai (jaise
/users,/products) - HTTP Methods use hote hain (GET, POST, PUT, DELETE)
- Data mostly JSON format mein aata hai
- Server ko yaad nahi rehta ki pehle kya hua — stateless
API = Waiter jo tumhare aur kitchen ke beech kaam karta hai. Tum menu (docs) dekho, order do (request), khaana aaye (response).
What is FastAPI?
FastAPI ek Python framework hai jo tumhe APIs banana sikhata hai — bahut fast aur easy tarike se.
Isko Sebastián Ramírez ne banaya hai aur ye ab Python ka sabse popular API framework ban raha hai.
FastAPI vs Flask vs Django
| Feature | FastAPI | Flask | Django |
|---|---|---|---|
| Speed | Bahut Fast | Medium | Slow |
| Async Support | Built-in | Nahi (extra lib) | Limited |
| Auto Docs | Haan (Swagger) | Nahi | Nahi (DRF mein hai) |
| Type Hints | Built-in | Nahi | Nahi |
| Learning Curve | Easy | Easy | Hard |
| Data Validation | Automatic (Pydantic) | Manual | Serializers |
| Best For | APIs | Small Apps | Full Websites |
Key Features
1. Async Support
Jab tumhara code kisi slow kaam ka wait kar raha hai (database, external API), tab doosre requests handle kar sakta hai. Ye async/await se hota hai.
Ek waiter ek time pe ek hi table serve kare = Synchronous (slow).
Ek waiter sabko order le le, phir sabka khaana laaye = Asynchronous (fast!).
2. Auto Documentation (Swagger)
FastAPI automatically tumhare APIs ka documentation bana deta hai. Tumhe kuch extra nahi karna. Sirf code likho — docs ready!
/docs— Swagger UI (interactive, test bhi kar sakte ho)/redoc— ReDoc (beautiful read-only docs)
3. Type Hints
Python mein type hints ka matlab hai batana ki variable ka type kya hai. FastAPI isko use karke automatic validation karta hai.
# Without type hints (Flask style)
def get_user(id): # id kya hai? string? int? pata nahi!
pass
# With type hints (FastAPI style)
def get_user(id: int): # id MUST be integer — automatic check!
pass
4. Speed
FastAPI NodeJS aur Go ke karib speed deta hai. Ye Starlette (web part) aur Pydantic (data validation) pe bana hai — dono blazing fast hain.
FastAPI = Fast (performance) + Fast (development speed) + Auto docs + Validation + Async. Beginner ke liye best choice.
Installation & Setup
Step 1: Python Install Karo
Sabse pehle check karo Python installed hai ya nahi:
python --version
# ya
python3 --version
Agar nahi hai to python.org se download karo. Python 3.8+ chahiye.
Install karte waqt "Add Python to PATH" checkbox zaroor tick karo. Nahi kiya to terminal mein python command nahi chalega.
Step 2: Virtual Environment Banao
Virtual Environment kya hai? Ye ek isolated box hai jisme tumhare project ke packages rehte hain. Har project ka apna alag box.
Virtual environment ek alag kamra hai. Ek kamre mein FastAPI hai, doosre mein Flask. Dono ek doosre ko disturb nahi karte.
# Apna project folder banao
mkdir my-fastapi-project
cd my-fastapi-project
# Virtual environment banao
python -m venv venv
# Activate karo
# Windows:
venv\Scripts\activate
# Mac/Linux:
source venv/bin/activate
# Tumhe dikhega: (venv) C:\my-fastapi-project>
Step 3: FastAPI Install Karo
pip install fastapi uvicorn
fastapi = ye framework hai. uvicorn = ye server hai jo tumhare code ko run karta hai.
FastAPI = tumhara khaana (code). Uvicorn = gas stove (server) jispe khaana pakta hai.
Step 4: Hello World API
Ek file banao main.py:
from fastapi import FastAPI
# FastAPI ka ek instance banao (ek naya app)
app = FastAPI()
# Ye tumhara pehla API endpoint hai!
@app.get("/")
def hello():
return {"message": "Hello World! Mera pehla API kaam kar raha hai!"}
Isko samjho line by line:
from fastapi import FastAPI— FastAPI library ko import karoapp = FastAPI()— Ek naya app banao (ye tumhara main object hai)@app.get("/")— Ye ek decorator hai. Matlab: jab koi/URL pe GET request bheje, ye function chalaodef hello()— Ye function response return karta haireturn {"message": "..."}}— JSON response bhejo
Step 5: Server Run Karo
uvicorn main:app --reload
Isko samjho:
| Part | Matlab |
|---|---|
uvicorn | Server start karo |
main | File ka naam (main.py) |
: | Separator |
app | FastAPI instance ka naam |
--reload | Code change hone pe automatically restart ho (development ke liye) |
Ab browser mein jao: http://127.0.0.1:8000
Tumhe dikhega:
{
"message": "Hello World! Mera pehla API kaam kar raha hai!"
}
Tumne apna pehla API bana liya! Ab http://127.0.0.1:8000/docs pe jao — Swagger documentation automatically ban gayi!
HTTP Methods (VERY DETAILED)
HTTP Methods batate hain ki tum server se kya karna chahte ho. Ye request ke sath jaata hai.
- GET = "Menu dikhao" (data lao)
- POST = "Ye order le lo" (naya data banao)
- PUT = "Order change karo — pura" (full update)
- PATCH = "Sirf extra cheese add karo" (partial update)
- DELETE = "Order cancel karo" (delete)
GET — Data Laao
GET request data laane ke liye hota hai. Browser mein jab URL type karte ho, wo GET request hai.
@app.get("/users")
def get_all_users():
return [
{"id": 1, "name": "Shweta", "email": "shweta@email.com"},
{"id": 2, "name": "Rahul", "email": "rahul@email.com"}
]
@app.get("/users/{user_id}")
def get_one_user(user_id: int):
return {"id": user_id, "name": "Shweta"}
POST — Naya Data Banao
POST request naya data create karne ke liye hota hai. Body mein data bhejte ho.
from pydantic import BaseModel
class User(BaseModel):
name: str
email: str
age: int
@app.post("/users")
def create_user(user: User):
# Yahan database mein save karoge
return {"message": "User created!", "user": user}
PUT — Pura Data Update Karo
PUT = Full replacement. Poora object bhejte ho, chahe sirf ek field change hui ho.
@app.put("/users/{user_id}")
def update_user(user_id: int, user: User):
# Poora user object replace ho jaayega
return {"message": f"User {user_id} updated!", "user": user}
PATCH — Partial Update
PATCH = Sirf jo change hua hai wo bhejo. Baaki cheezein same rahein.
from typing import Optional
class UserUpdate(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
age: Optional[int] = None
@app.patch("/users/{user_id}")
def partial_update(user_id: int, user: UserUpdate):
return {"message": f"User {user_id} partially updated!"}
DELETE — Data Hatao
@app.delete("/users/{user_id}")
def delete_user(user_id: int):
# Database se delete karo
return {"message": f"User {user_id} deleted!"}
HTTP Status Codes
Har response ke sath ek number aata hai jo batata hai kya hua:
| Code | Naam | Matlab (Simple) |
|---|---|---|
| 200 | OK | Sab theek hai, data aa gaya |
| 201 | Created | Naya data ban gaya |
| 204 | No Content | Delete ho gaya, kuch return nahi |
| 400 | Bad Request | Tumne galat data bheja |
| 401 | Unauthorized | Login nahi kiya |
| 403 | Forbidden | Login kiya but permission nahi hai |
| 404 | Not Found | Ye URL exist nahi karta |
| 500 | Internal Server Error | Server mein kuch toot gaya (backend ka bug) |
- 2xx = Sab badhiya ✔
- 4xx = Tumhari galti (client error)
- 5xx = Server ki galti (backend error)
Headers Kya Hain?
Headers = request ya response ke sath extra information. Jaise letter ke sath envelope pe likha hota hai — sender, date, etc.
| Header | Matlab |
|---|---|
Content-Type: application/json | Body mein JSON data hai |
Authorization: Bearer <token> | Login ka proof (JWT token) |
Accept: application/json | Mujhe JSON response chahiye |
What is Postman? (Complete Guide)
Postman Kya Hai?
Postman ek tool hai jisse tum APIs ko test kar sakte ho bina frontend banaye.
Imagine karo tumne kitchen banayi (backend API). Ab tum khud test karna chahte ho ki khaana sahi ban raha hai ya nahi — bina restaurant open kiye. Postman wo taste-testing tool hai.
Why Postman?
- Browser sirf GET request bhej sakta hai (URL type karke)
- POST, PUT, DELETE test karne ke liye Postman chahiye
- Body, Headers, Authorization — sab set kar sakte ho
- Requests save karke rakh sakte ho (Collections)
- Free hai!
Step-by-Step Postman Guide
Download and Install
Go to postman.com/downloads → Download → Install → Open → Create free account (ya skip karke use karo).
Interface Samjho
Jab Postman khulega, tum ye dekhoge:
- Left side: Collections (saved requests ka folder)
- Center top: URL bar + Method dropdown (GET/POST/etc.)
- Center middle: Tabs for Params, Headers, Body, etc.
- Bottom: Response area (server ka jawab yahan dikhega)
GET Request Bhejo
- Pehle apna FastAPI server run karo:
uvicorn main:app --reload - Postman mein method dropdown se GET select karo
- URL mein type karo:
http://127.0.0.1:8000/users - Blue "Send" button dabao
- Neeche response dikhega — JSON data!
POST Request Bhejo (Data Create Karo)
- Method dropdown se POST select karo
- URL:
http://127.0.0.1:8000/users - Neeche "Body" tab pe click karo
- "raw" select karo
- Right side mein dropdown se "JSON" select karo
- Body mein JSON likho:
{
"name": "Shweta",
"email": "shweta@email.com",
"age": 25
}
Phir Send dabao. Response mein "User created!" dikhega.
PUT Request (Update)
- Method: PUT
- URL:
http://127.0.0.1:8000/users/1 - Body (raw JSON): Poora updated user object bhejo
- Send
DELETE Request
- Method: DELETE
- URL:
http://127.0.0.1:8000/users/1 - Body ki zaroorat nahi — sirf Send dabao
Headers Add Karna
"Headers" tab pe click karo. Key-value pairs add karo:
| Key | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer your-jwt-token-here |
Response Padhna
Neeche response area mein ye dekhoge:
- Body: Server ka response data (JSON)
- Status: 200 OK, 404 Not Found, etc.
- Time: Kitna time laga (ms mein)
- Size: Response kitna bada hai
Collections Mein Save Karo
- Request bhejne ke baad "Save" button dabao (ya Ctrl+S)
- "Create Collection" pe click karo
- Collection ka naam do: "My FastAPI Project"
- Request ka naam do: "Get All Users"
- Save karo — ab left side mein dikhai dega
Aise sabhi requests save karo. Baad mein ek click mein phir test kar sakte ho!
FastAPI ka /docs bhi Postman jaisa kaam karta hai! Browser mein http://127.0.0.1:8000/docs kholo — wahaan se bhi test kar sakte ho, bina Postman ke.
Path Parameters & Query Parameters
Path Parameters
Path parameter URL ke andar hota hai. Ye ek specific cheez identify karta hai.
# /users/5 — yahan 5 ek path parameter hai
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"user_id": user_id, "name": "Shweta"}
# /products/laptop — yahan "laptop" path parameter hai
@app.get("/products/{product_name}")
def get_product(product_name: str):
return {"product": product_name}
Query Parameters
Query parameter URL ke baad ? ke baad aate hain. Ye filtering/searching ke liye use hote hain.
# /users?name=shweta&age=25
@app.get("/users")
def search_users(name: str = None, age: int = None):
return {"name": name, "age": age}
# /products?category=electronics&min_price=1000&max_price=5000
@app.get("/products")
def filter_products(
category: str = None,
min_price: float = 0,
max_price: float = 999999
):
return {
"category": category,
"price_range": f"{min_price} - {max_price}"
}
Difference Table
| Feature | Path Parameter | Query Parameter |
|---|---|---|
| URL mein kahan | /users/5 | /users?name=shweta |
| Required? | Haan, always | Nahi, optional ho sakta |
| Kab use karo | Specific item (by ID) | Filter/Search/Pagination |
| Example | User #5 laao | Sab users jinka age > 20 |
Dono Sath Mein
# /users/5/orders?status=delivered&page=1
@app.get("/users/{user_id}/orders")
def get_user_orders(
user_id: int, # path parameter
status: str = None, # query parameter (optional)
page: int = 1 # query parameter (default=1)
):
return {
"user_id": user_id,
"status_filter": status,
"page": page
}
Agar ek specific cheez chahiye → Path Parameter (/users/5). Agar filter/search karna hai → Query Parameter (/users?age=25).
Request Body (Pydantic Models)
Request Body Kya Hai?
Jab tum POST ya PUT request bhejte ho, tumhe data bhejna hota hai. Wo data request body mein jaata hai. Ye JSON format mein hota hai.
Path/Query parameters = envelope pe likha address. Request Body = envelope ke andar ka letter (actual content).
Pydantic Kya Hai?
Pydantic ek library hai jo data validate karti hai. Matlab check karti hai ki data sahi format mein hai ya nahi.
FastAPI mein BaseModel se class banate ho — ye tumhara data ka template hai.
from pydantic import BaseModel
class User(BaseModel):
name: str # name MUST be string
email: str # email MUST be string
age: int # age MUST be integer
is_active: bool = True # default True, optional
Ab agar koi age: "hello" bheje, FastAPI automatically error dega! Tumhe kuch check nahi karna.
Using in Routes
@app.post("/users")
def create_user(user: User):
# user.name, user.email, user.age — sab accessible hai
return {
"message": f"Welcome {user.name}!",
"data": user.model_dump() # dict mein convert
}
Postman se test karo:
// Body → raw → JSON
{
"name": "Shweta",
"email": "shweta@email.com",
"age": 25
}
Field Validation
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(
min_length=2,
max_length=50,
description="User ka naam"
)
email: str = Field(
pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$",
description="Valid email address"
)
age: int = Field(
ge=1, # greater than or equal to 1
le=120, # less than or equal to 120
description="Age 1-120 ke beech"
)
salary: float = Field(
gt=0, # greater than 0
description="Positive salary"
)
Optional Fields
from typing import Optional
class UserUpdate(BaseModel):
name: Optional[str] = None # bhejo ya na bhejo
email: Optional[str] = None
age: Optional[int] = None
phone: str = "Not provided" # default value
Nested Models (Model ke andar Model)
class Address(BaseModel):
street: str
city: str
pincode: str
class User(BaseModel):
name: str
email: str
address: Address # Nested model!
JSON body aise dikhegi:
{
"name": "Shweta",
"email": "shweta@email.com",
"address": {
"street": "MG Road",
"city": "Mumbai",
"pincode": "400001"
}
}
Response Models
response_model Kya Hai?
Jab tum data bhejte ho response mein, response_model batata hai ki kaunsa data bhejana hai aur kaunsa nahi.
Database mein password stored hai. Lekin response mein password nahi bhejna — response_model filter ka kaam karta hai.
# Ye model data RECEIVE karne ke liye (input)
class UserCreate(BaseModel):
name: str
email: str
password: str # password input mein aayega
# Ye model data SEND karne ke liye (output)
class UserResponse(BaseModel):
id: int
name: str
email: str
# password NAHI hai — response mein nahi jaayega!
@app.post("/users", response_model=UserResponse)
def create_user(user: UserCreate):
# Database mein save karo (including password)
# But response mein sirf name, email jaayega
new_user = {"id": 1, "name": user.name, "email": user.email}
return new_user
List Response
from typing import List
@app.get("/users", response_model=List[UserResponse])
def get_all_users():
return [
{"id": 1, "name": "Shweta", "email": "s@e.com"},
{"id": 2, "name": "Rahul", "email": "r@e.com"}
]
Input model (request) aur Output model (response) alag rakho. Kabhi password response mein mat bhejo!
Database Connection (SQLAlchemy)
ORM Kya Hai?
ORM = Object Relational Mapping. Ye ek tarika hai database ke sath kaam karne ka bina SQL likhein.
Bina ORM: SELECT * FROM users WHERE id = 5 (raw SQL)
ORM se: db.query(User).filter(User.id == 5).first() (Python!)
ORM tumhare Python objects ko automatically SQL queries mein convert karta hai.
Install Packages
# SQLAlchemy (ORM) + SQLite (database)
pip install sqlalchemy
# MySQL ke liye extra
pip install pymysql
# PostgreSQL ke liye extra
pip install psycopg2-binary
database.py — Database Setup
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# SQLite (file-based, easy, no setup needed)
SQLALCHEMY_DATABASE_URL = "sqlite:///./myapp.db"
# MySQL example:
# SQLALCHEMY_DATABASE_URL = "mysql+pymysql://user:password@localhost/dbname"
# Engine = database se connection
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
connect_args={"check_same_thread": False} # sirf SQLite ke liye
)
# Session = ek conversation with database
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Base = sabhi models ka parent
Base = declarative_base()
models.py — Database Tables
from sqlalchemy import Column, Integer, String, Boolean
from database import Base
class User(Base):
__tablename__ = "users" # table ka naam
id = Column(Integer, primary_key=True, index=True)
name = Column(String(50), nullable=False)
email = Column(String(100), unique=True, index=True)
password = Column(String(100))
is_active = Column(Boolean, default=True)
Session Management (Dependency)
# main.py mein
from database import SessionLocal, engine, Base
# Tables create karo (pehli baar)
Base.metadata.create_all(bind=engine)
# Har request ke liye ek session do, baad mein band karo
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
SQLite = Ek file hai (myapp.db), koi setup nahi. Learning ke liye perfect.
MySQL = Alag server chalta hai, production ke liye better. Connection string change karo bas.
CRUD Operations (Complete Working Example)
CRUD = Create, Read, Update, Delete. Ye har application ka base hai.
schemas.py (Pydantic Models)
from pydantic import BaseModel
from typing import Optional
class UserCreate(BaseModel):
name: str
email: str
password: str
class UserResponse(BaseModel):
id: int
name: str
email: str
is_active: bool
class Config:
from_attributes = True # SQLAlchemy object se convert
class UserUpdate(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
main.py (All CRUD Routes)
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
import models, schemas
from database import SessionLocal, engine, Base
Base.metadata.create_all(bind=engine)
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# ==========================================
# CREATE - Naya user banao
# ==========================================
@app.post("/users", response_model=schemas.UserResponse, status_code=201)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
# Check: email pehle se toh nahi hai?
existing = db.query(models.User).filter(
models.User.email == user.email
).first()
if existing:
raise HTTPException(status_code=400, detail="Email already registered")
# Naya user banao
new_user = models.User(
name=user.name,
email=user.email,
password=user.password # real mein hash karo!
)
db.add(new_user)
db.commit()
db.refresh(new_user)
return new_user
# ==========================================
# READ ALL - Sab users laao (with pagination)
# ==========================================
@app.get("/users", response_model=List[schemas.UserResponse])
def get_all_users(
skip: int = 0, # kitne skip karo (pagination)
limit: int = 10, # kitne laao (max)
db: Session = Depends(get_db)
):
users = db.query(models.User).offset(skip).limit(limit).all()
return users
# ==========================================
# READ ONE - Ek specific user laao (by ID)
# ==========================================
@app.get("/users/{user_id}", response_model=schemas.UserResponse)
def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
# ==========================================
# UPDATE - User ko update karo
# ==========================================
@app.put("/users/{user_id}", response_model=schemas.UserResponse)
def update_user(
user_id: int,
user_data: schemas.UserUpdate,
db: Session = Depends(get_db)
):
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Sirf wo fields update karo jo bheje hain
update_data = user_data.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(user, key, value)
db.commit()
db.refresh(user)
return user
# ==========================================
# DELETE - User ko delete karo
# ==========================================
@app.delete("/users/{user_id}", status_code=204)
def delete_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
db.delete(user)
db.commit()
return None
POST /users → user banao → GET /users → sab dekho → GET /users/1 → ek dekho → PUT /users/1 → update → DELETE /users/1 → delete.
Error Handling
HTTPException
Jab kuch galat ho, tum HTTPException raise karte ho. Ye ek error response bhejta hai proper status code ke sath.
from fastapi import HTTPException
@app.get("/users/{user_id}")
def get_user(user_id: int):
if user_id < 1:
raise HTTPException(
status_code=400,
detail="User ID must be positive"
)
if user_id > 1000:
raise HTTPException(
status_code=404,
detail="User not found"
)
return {"id": user_id, "name": "Shweta"}
Custom Error Response
raise HTTPException(
status_code=403,
detail={
"error": "Access Denied",
"message": "Tumhe isko access karne ki permission nahi hai"
},
headers={"X-Error": "Permission denied"}
)
Validation Errors (Automatic!)
Agar koi galat data bheje (jaise age mein string), FastAPI automatically 422 error dega:
{
"detail": [
{
"loc": ["body", "age"],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
Try-Except in Routes
@app.get("/divide/{a}/{b}")
def divide(a: int, b: int):
try:
result = a / b
return {"result": result}
except ZeroDivisionError:
raise HTTPException(
status_code=400,
detail="Zero se divide nahi kar sakte bhai!"
)
Global Exception Handler
from fastapi import Request
from fastapi.responses import JSONResponse
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={
"error": "Internal Server Error",
"message": "Kuch toh gadbad hai! Hum check kar rahe hain."
}
)
Middleware
Middleware Kya Hai?
Middleware ek security guard hai jo har request aur response ke beech baitha hai. Har request middleware se hokar jaati hai.
CORS Middleware (Most Important!)
CORS = Cross-Origin Resource Sharing. Jab tumhara frontend (localhost:3000) backend (localhost:8000) se baat kare, browser block kar deta hai. CORS middleware allow karta hai.
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # frontend URL
allow_credentials=True,
allow_methods=["*"], # GET, POST, PUT, DELETE sab allow
allow_headers=["*"], # sab headers allow
)
Custom Middleware (Logging)
import time
from fastapi import Request
@app.middleware("http")
async def log_requests(request: Request, call_next):
# Request aane se PEHLE
start_time = time.time()
method = request.method
url = request.url.path
# Route handler ko call karo
response = await call_next(request)
# Response jaane se PEHLE
duration = time.time() - start_time
print(f"{method} {url} - {response.status_code} - {duration:.3f}s")
return response
- CORS — Jab frontend alag port pe ho
- Logging — Har request log karna ho
- Authentication — Har request mein token check karna ho
- Rate Limiting — Too many requests rok na ho
Authentication (JWT)
Authentication vs Authorization
Authentication (Kaun ho tum?)
Ye verify karta hai ki tum kaun ho.
Example: Login karna (username + password)
Authorization (Kya kar sakte ho?)
Ye check karta hai ki tumhe permission hai ya nahi.
Example: Admin page access sirf admin ko mile
JWT Token Kya Hai?
JWT = JSON Web Token. Ye ek pass hai jo login ke baad milta hai.
Tum ticket counter pe jaate ho (login), ID dikhate ho (password), ticket milti hai (JWT token). Ab har theater hall mein ticket dikhate ho (har request mein token bhejte ho). Security check karta hai ticket valid hai ya nahi.
Install Packages
pip install python-jose[cryptography] passlib[bcrypt]
Password Hashing
from passlib.context import CryptContext
# Password hasher setup
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Password hash karo (save karne se pehle)
def hash_password(password: str) -> str:
return pwd_context.hash(password)
# Password verify karo (login ke time)
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
Kabhi bhi password directly database mein mat save karo. Hamesha hash karo. Hash = one-way encryption. "password123" → "$2b$12$LJ3..."
JWT Token Create & Verify
from jose import JWTError, jwt
from datetime import datetime, timedelta
SECRET_KEY = "tumhara-secret-key-bahut-lamba-rakho"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
token = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return token
def verify_token(token: str):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
Login Route
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
@app.post("/login")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Database se user dhoondho
user = find_user_by_email(form_data.username) # implement karo
if not user or not verify_password(form_data.password, user.password):
raise HTTPException(status_code=401, detail="Galat email ya password")
# Token banao
token = create_access_token({"sub": user.email})
return {"access_token": token, "token_type": "bearer"}
# Protected Route
def get_current_user(token: str = Depends(oauth2_scheme)):
payload = verify_token(token)
email = payload.get("sub")
if not email:
raise HTTPException(status_code=401, detail="Invalid token")
return email
@app.get("/profile")
def my_profile(current_user: str = Depends(get_current_user)):
return {"email": current_user, "message": "Welcome to your profile!"}
File Upload
Install Extra
pip install python-multipart
Basic File Upload
from fastapi import UploadFile, File
import shutil
from pathlib import Path
# Uploads folder banao
UPLOAD_DIR = Path("uploads")
UPLOAD_DIR.mkdir(exist_ok=True)
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
# File ko disk pe save karo
file_path = UPLOAD_DIR / file.filename
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {
"filename": file.filename,
"size": file.size,
"content_type": file.content_type,
"message": "File uploaded successfully!"
}
File Validation (Size + Type)
ALLOWED_TYPES = ["image/jpeg", "image/png", "image/gif"]
MAX_SIZE = 5 * 1024 * 1024 # 5 MB
@app.post("/upload-image")
async def upload_image(file: UploadFile = File(...)):
# Type check
if file.content_type not in ALLOWED_TYPES:
raise HTTPException(
status_code=400,
detail=f"Sirf images allowed hain! (JPEG, PNG, GIF)"
)
# Size check
contents = await file.read()
if len(contents) > MAX_SIZE:
raise HTTPException(
status_code=400,
detail="File 5MB se badi hai!"
)
# Save
file_path = UPLOAD_DIR / file.filename
with open(file_path, "wb") as f:
f.write(contents)
return {"message": "Image uploaded!", "filename": file.filename}
Multiple Files
from typing import List
@app.post("/upload-multiple")
async def upload_many(files: List[UploadFile] = File(...)):
filenames = []
for file in files:
file_path = UPLOAD_DIR / file.filename
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
filenames.append(file.filename)
return {"uploaded": filenames, "count": len(filenames)}
Dependency Injection
DI Kya Hai? (Simple Explanation)
Dependency Injection = Kisi cheez ko function ke andar automatically dena, bina baar baar likhein.
Har room mein light switch hai. Tum switch dabate ho, bijli aati hai. Tumhe bijli station se wire nahi laani. Bijli automatically inject hoti hai.
Waise hi Depends(get_db) se database session automatically inject hota hai har route mein.
Database Session as Dependency
from fastapi import Depends
# Ye dependency hai — har route ko db session deta hai
def get_db():
db = SessionLocal()
try:
yield db # db session do
finally:
db.close() # kaam hone pe band karo
# Route mein use karo — db automatically milta hai!
@app.get("/users")
def get_users(db: Session = Depends(get_db)):
return db.query(User).all()
Custom Dependencies
# Pagination dependency
def pagination_params(page: int = 1, size: int = 10):
return {"skip": (page - 1) * size, "limit": size}
@app.get("/products")
def get_products(pagination: dict = Depends(pagination_params)):
# pagination = {"skip": 0, "limit": 10}
return db.query(Product).offset(pagination["skip"]).limit(pagination["limit"]).all()
# Auth dependency — har protected route mein use karo
def require_admin(current_user = Depends(get_current_user)):
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Admin only!")
return current_user
@app.delete("/admin/users/{id}")
def admin_delete(id: int, admin = Depends(require_admin)):
# Sirf admin yahan aa sakta hai
pass
- DRY — Same code baar baar nahi likhna (database connection, auth check)
- Testable — Testing mein dependency ko mock kar sakte ho
- Clean — Route function sirf apna kaam kare, baaki Depends handle kare
Project Structure (Best Practice)
Jab project bada hota hai, sab kuch ek file mein nahi rakh sakte. Files ko organize karo:
Router Use Karo (Alag files mein routes)
routes/users.py
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
router = APIRouter(
prefix="/users", # sab routes /users se start honge
tags=["Users"] # Swagger docs mein group dikhega
)
@router.get("/")
def get_users(db: Session = Depends(get_db)):
return db.query(User).all()
@router.post("/")
def create_user(user: UserCreate, db: Session = Depends(get_db)):
# ... create logic
pass
main.py
from fastapi import FastAPI
from routes import users, products, auth
app = FastAPI(title="My Awesome API")
# Routers include karo
app.include_router(users.router)
app.include_router(products.router)
app.include_router(auth.router)
@app.get("/")
def root():
return {"message": "API is running!"}
- Organization — Users ka code users.py mein, products ka products.py mein
- Team Work — Ek banda users pe kaam kare, doosra products pe
- Readability — 100-line file padhna easy hai, 5000-line file mushkil
Swagger UI / Docs
Auto Documentation
FastAPI ka sabse amazing feature — documentation automatically banti hai! Kuch nahi karna.
| URL | Kya Hai | Use |
|---|---|---|
/docs | Swagger UI | Interactive — test bhi kar sakte ho |
/redoc | ReDoc | Beautiful read-only documentation |
/openapi.json | OpenAPI Schema | Raw JSON schema (tools ke liye) |
Swagger Se API Test Kaise Karo
- Browser mein
http://127.0.0.1:8000/docskholo - Tumhe sab routes dikhenge, colors ke sath (GET=green, POST=blue, etc.)
- Kisi bhi route pe click karo → expand hoga
- "Try it out" button dabao
- Agar body chahiye to JSON bharo
- "Execute" dabao
- Neeche response dikhega — status code, body, headers sab
Docs Customize Karo
app = FastAPI(
title="Shweta's API",
description="Mera pehla FastAPI project! Sab kuch yahan hai.",
version="1.0.0",
docs_url="/docs", # ya None to disable
redoc_url="/redoc", # ya None to disable
)
Swagger docs = free Postman! Bina Postman install kiye browser mein hi sab test kar sakte ho. Plus ye automatically update hota hai jab code change hota hai.
Deployment Basics
Uvicorn Kya Hai?
Uvicorn = ASGI server. Ye tumhare Python code ko HTTP requests handle karne laayak banata hai. Development mein directly use karte hain.
# Development
uvicorn main:app --reload --host 0.0.0.0 --port 8000
Gunicorn Kya Hai?
Gunicorn = Production server. Ye Uvicorn ke manager jaisa hai — multiple workers chalata hai.
# Production (4 workers)
pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
Uvicorn (Development)
Single process. --reload supported. Fast to start. Good for coding.
Gunicorn (Production)
Multiple workers. No reload. Handles more traffic. Crash recovery.
Docker Basics
Docker = tumhara code ek box mein pack karo. Kisi bhi machine pe chalega, bina "mere machine pe toh chal raha tha" wala problem.
requirements.txt
fastapi==0.109.0
uvicorn==0.27.0
sqlalchemy==2.0.25
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.6
Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# Docker image banao
docker build -t my-fastapi-app .
# Container chalao
docker run -p 8000:8000 my-fastapi-app
Environment Variables
import os
from dotenv import load_dotenv
load_dotenv() # .env file se variables load karo
SECRET_KEY = os.getenv("SECRET_KEY", "default-key")
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./app.db")
.env file
SECRET_KEY=mera-bahut-lamba-secret-key-yahan
DATABASE_URL=mysql+pymysql://user:pass@localhost/mydb
DEBUG=True
.gitignore mein .env zaroor dalo. Secret keys git mein nahi jaani chahiye.
Common Interview Questions (Top 20)
/users/5) — required hota hai, specific resource identify karta hai. Query parameter URL ke baad ? ke baad aata hai (/users?age=25) — optional hota hai, filtering/searching ke liye use hota hai.Depends() function se dependencies inject karte hain. Common use: database session, authentication, pagination parameters.async def use karo. CPU-heavy kaam (calculations) ke liye normal def better hai. Async se server busy hone pe bhi doosri requests handle kar sakta hai.raise HTTPException(status_code=404, detail="Not found") karte hain. Ye proper HTTP error response bhejta hai.@app.middleware("http") se custom middleware bana sakte ho.db.query(User).filter(User.id == 5).first() internally SELECT * FROM users WHERE id = 5 ban jaata hai.UploadFile class se. python-multipart package install karo. Route mein file: UploadFile = File(...) parameter do. File attributes: filename, content_type, size. file.read() se content padho aur disk pe save karo.app.include_router(users.router) se include karte hain. Prefix aur tags bhi set kar sakte hain.BackgroundTasks se ye kaam schedule kar sakte ho. Response pehle jaata hai, kaam background mein hota hai. User ko wait nahi karna padta.--reload flag se auto-restart hota hai. Production mein Gunicorn ke sath use karte hain multiple workers ke liye.status_code parameter se set karte hain.TestClient use karte hain (from fastapi.testclient). Ye internally requests library use karta hai. client = TestClient(app) bana ke client.get("/users") se test karte hain. Pytest framework ke sath commonly use hota hai.from typing import Optional import karke field_name: Optional[str] = None likhte hain. Ye field optional ban jaata hai — request mein bhejo ya na bhejo, dono chalega. Default value None hogi agar nahi bheja.HTTPException use karo proper status codes ke sath. 2) Custom exception handlers banao for global error handling. 3) Pydantic validation errors ko customize karo. 4) Try-except mein specific exceptions catch karo (bare except nahi). 5) Meaningful error messages do. 6) Production mein stack traces client ko mat bhejo.Quick Reference Card (Cheat Sheet)
Decorators
@app.get("/path")
@app.post("/path")
@app.put("/path")
@app.patch("/path")
@app.delete("/path")
@app.middleware("http")
@app.exception_handler(Exc)
@app.on_event("startup")
@app.on_event("shutdown")
Common Imports
from fastapi import FastAPI
from fastapi import Depends
from fastapi import HTTPException
from fastapi import APIRouter
from fastapi import UploadFile, File
from fastapi import Query, Path, Body
from fastapi import BackgroundTasks
from pydantic import BaseModel, Field
from typing import Optional, List
Status Codes
200 OK (GET success)
201 Created (POST success)
204 No Content (DELETE success)
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
422 Validation Error
500 Internal Server Error
Pydantic Quick
name: str
age: int
price: float
is_active: bool
tags: List[str]
name: Optional[str] = None
age: int = Field(ge=1, le=120)
email: str = Field(pattern=r"...")
user.model_dump()
Database (SQLAlchemy)
db.query(Model).all()
db.query(Model).first()
db.query(M).filter(M.id==x)
db.add(new_object)
db.commit()
db.refresh(object)
db.delete(object)
.offset(skip).limit(limit)
Terminal Commands
pip install fastapi uvicorn
uvicorn main:app --reload
python -m venv venv
source venv/bin/activate
pip freeze > requirements.txt
pip install -r requirements.txt
docker build -t app .
docker run -p 8000:8000 app
Quick CRUD Template
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel
from typing import Optional, List
app = FastAPI()
# ---- Schema ----
class ItemCreate(BaseModel):
name: str
price: float
class ItemResponse(BaseModel):
id: int
name: str
price: float
# ---- In-memory DB (testing ke liye) ----
items_db = []
counter = 0
# ---- Routes ----
@app.post("/items", response_model=ItemResponse, status_code=201)
def create(item: ItemCreate):
global counter
counter += 1
new = {"id": counter, **item.model_dump()}
items_db.append(new)
return new
@app.get("/items", response_model=List[ItemResponse])
def read_all():
return items_db
@app.get("/items/{id}", response_model=ItemResponse)
def read_one(id: int):
item = next((i for i in items_db if i["id"] == id), None)
if not item:
raise HTTPException(404, "Not found")
return item
@app.delete("/items/{id}", status_code=204)
def delete(id: int):
global items_db
items_db = [i for i in items_db if i["id"] != id]
Ab tum FastAPI ke sab basics jaante ho. Practice karo, projects banao, aur interview mein rock karo! 💪
Made with care. Keep learning, keep building.