Responses และ Status Codes
การส่งคืน JSON
หัวข้อที่มีชื่อว่า “การส่งคืน JSON”ใน Express คุณเรียก res.json(data) ใน FastAPI คุณแค่ return Python dict, list หรือ Pydantic model จาก handler function — FastAPI serialize เป็น JSON อัตโนมัติ
// Expressapp.get('/items/:id', (req, res) => { res.json({ id: Number(req.params.id), name: 'Widget' });});
// พร้อม status codeapp.post('/items', (req, res) => { res.status(201).json({ id: 42, ...req.body });});# FastAPIfrom fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")async def get_item(item_id: int): return {"id": item_id, "name": "Widget"} # auto-serialize
@app.post("/items", status_code=201) # status เริ่มต้นผ่าน decoratorasync def create_item(item: dict): return {"id": 42, **item}response_model — กรองและกำหนดรูปร่าง output
หัวข้อที่มีชื่อว่า “response_model — กรองและกำหนดรูปร่าง output”response_model คือ argument ของ decorator ที่บอก FastAPI ว่าจะ serialize response ด้วย Pydantic model ตัวไหน จากนั้น FastAPI จะตัด field ที่ไม่อยู่ใน model ทิ้ง ซึ่งมีประโยชน์มากเวลาต้องซ่อน password หรือ field ภายใน พร้อมกับ validate ข้อมูลขาออกให้ด้วย
// TypeScript — เลือก fields เองinterface ItemPublic { id: number; name: string; price: number; }
app.get('/items/:id', (req, res) => { const dbRow = getFromDB(req.params.id); const { id, name, price } = dbRow; // strip secret fields เอง res.json({ id, name, price } as ItemPublic);});# FastAPI response_model strip fields อัตโนมัติfrom fastapi import FastAPIfrom pydantic import BaseModel
class ItemInDB(BaseModel): # full model พร้อม internal fields id: int name: str price: float hashed_password: str # ไม่ควรส่งกลับ
class ItemPublic(BaseModel): # model ที่ปลอดภัยสำหรับ public id: int name: str price: float
app = FastAPI()
@app.get("/items/{item_id}", response_model=ItemPublic)async def get_item(item_id: int): # hashed_password จะถูก strip อัตโนมัติ return ItemInDB(id=item_id, name="Widget", price=9.99, hashed_password="secret")Status codes
หัวข้อที่มีชื่อว่า “Status codes”FastAPI expose status module จาก Starlette สำหรับ named constants เทียบเท่ากับ http-status package ของ Node
// Express พร้อม http-statusimport status from 'http-status';res.status(status.CREATED).json(data); // 201res.status(status.NO_CONTENT).send(); // 204res.status(status.NOT_FOUND).json({...}); // 404# FastAPI status constantsfrom fastapi import FastAPIfrom fastapi import status
app = FastAPI()
@app.post("/items", status_code=status.HTTP_201_CREATED)async def create_item(item: dict): return item
@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)async def delete_item(item_id: int): return None # 204 — ไม่มี bodyJSONResponse — ควบคุมโดยตรง
หัวข้อที่มีชื่อว่า “JSONResponse — ควบคุมโดยตรง”เมื่อต้องการตั้ง custom headers หรือ override status code แบบ dynamic ใช้ JSONResponse โดยตรง เทียบเท่ากับการสร้าง res object เอง ใน Express
// Express — set headers เองapp.get('/download', (req, res) => { res.set('X-Custom-Header', 'value'); res.status(200).json({ url: 'https://...' });});# FastAPI JSONResponsefrom fastapi import FastAPIfrom fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/download")async def download(): return JSONResponse( content={"url": "https://example.com/file.zip"}, status_code=200, headers={"X-Custom-Header": "value"}, )ลองเล่น — JSON serialization ด้วย stdlib
หัวข้อที่มีชื่อว่า “ลองเล่น — JSON serialization ด้วย stdlib”รันที่เครื่องตัวเองสำหรับ FastAPI response จริง snippet ด้านล่างแสดง
jsonmodule ของ Python ซึ่ง FastAPI ใช้ภายใต้ hood
import jsonfrom datetime import datetime
# จำลอง output ของ Pydantic model's .model_dump()item = { "id": 1, "name": "Widget", "price": 9.99, "in_stock": True, "tags": ["sale", "new"], "created_at": datetime.utcnow().isoformat() + "Z",}
# Serialize — สิ่งที่ FastAPI ส่งผ่าน wireencoded = json.dumps(item, indent=2)print("Response body:")print(encoded)
# Deserialize — สิ่งที่ client ได้รับdecoded = json.loads(encoded)print(f"\nDecoded type: {type(decoded)}")print(f"name={decoded['name']}, price={decoded['price']}")
# response_model filtering equivalentpublic_fields = {"id", "name", "price"}filtered = {k: v for k, v in decoded.items() if k in public_fields}print(f"\nFiltered (response_model): {filtered}")Loading Python runtime (first run only)…