Chuyển đến nội dung chính

Bài 3: Cài đặt và Khởi tạo FastAPI Project

Cài đặt FastAPI và Uvicorn, khởi tạo project structure, hiểu cấu trúc thư mục chuẩn. Chạy development server, Swagger UI, ReDoc và viết API endpoint đầu tiên.

💻 Lập trình — Bài 3 Bài 3: Cài đặt và Khởi tạo FastAPI Project

Python FastAPI: Từ Cơ bản đến Nâng cao

Phần 1: Nền tảng Python & FastAPI

xdev.asia

1. Cài đặt môi trường phát triển

Yêu cầu hệ thống

  • Python 3.12+ (khuyến nghị 3.12 hoặc 3.13)
  • uv hoặc Poetry cho dependency management
  • VS Code hoặc PyCharm với Python extension
  • Git cho version control

Cài đặt Python 3.12+

# macOS (Homebrew)
brew install [email protected]

# Ubuntu/Debian
sudo apt update
sudo apt install python3.12 python3.12-venv python3.12-dev

# Windows - tải từ https://python.org

# Kiểm tra version
python3 --version
# Python 3.12.x

Cài đặt uv (Recommended)

# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# Kiểm tra
uv --version

2. Khởi tạo FastAPI Project

# Tạo project mới
uv init fastapi-tutorial
cd fastapi-tutorial

# Thêm FastAPI và Uvicorn
uv add fastapi "uvicorn[standard]"

# Thêm dev dependencies
uv add --dev ruff mypy pytest httpx

# Xem project structure
tree .

File pyproject.toml sau khi khởi tạo:

[project]
name = "fastapi-tutorial"
version = "0.1.0"
description = "FastAPI Tutorial - From Basic to Advanced"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "fastapi>=0.115.0",
    "uvicorn[standard]>=0.32.0",
]

[dependency-groups]
dev = [
    "httpx>=0.28.0",
    "mypy>=1.13.0",
    "pytest>=8.3.0",
    "ruff>=0.8.0",
]

[tool.ruff]
target-version = "py312"
line-length = 120

[tool.ruff.lint]
select = ["E", "F", "I", "N", "W", "UP", "B", "A", "SIM", "TCH"]

[tool.mypy]
python_version = "3.12"
strict = true

3. Cấu trúc Project

Tạo cấu trúc thư mục chuẩn:

# Tạo cấu trúc thư mục
mkdir -p app/{api/v1,models,schemas,services,core}
touch app/__init__.py app/main.py app/config.py
touch app/api/__init__.py app/api/v1/__init__.py
touch app/models/__init__.py app/schemas/__init__.py
touch app/services/__init__.py app/core/__init__.py
fastapi-tutorial/
├── pyproject.toml
├── uv.lock
├── .env
├── .gitignore
├── app/
│   ├── __init__.py
│   ├── main.py              # Entry point
│   ├── config.py            # Configuration
│   ├── api/
│   │   ├── __init__.py
│   │   └── v1/
│   │       ├── __init__.py
│   │       └── items.py     # Item routes
│   ├── models/
│   │   └── __init__.py
│   ├── schemas/
│   │   ├── __init__.py
│   │   └── item.py          # Pydantic schemas
│   ├── services/
│   │   └── __init__.py
│   └── core/
│       └── __init__.py
└── tests/
    └── __init__.py

4. Viết FastAPI Application đầu tiên

app/config.py - Configuration

from pydantic_settings import BaseSettings


class Settings(BaseSettings):
    app_name: str = "FastAPI Tutorial"
    app_version: str = "0.1.0"
    debug: bool = True

    model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}


settings = Settings()

app/schemas/item.py - Pydantic Schemas

from pydantic import BaseModel, Field


class ItemBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=100, examples=["Laptop"])
    description: str | None = Field(None, max_length=500, examples=["A powerful laptop"])
    price: float = Field(..., gt=0, examples=[999.99])
    is_available: bool = Field(True)


class ItemCreate(ItemBase):
    pass


class ItemUpdate(BaseModel):
    name: str | None = Field(None, min_length=1, max_length=100)
    description: str | None = Field(None, max_length=500)
    price: float | None = Field(None, gt=0)
    is_available: bool | None = None


class ItemResponse(ItemBase):
    id: int

    model_config = {"from_attributes": True}

app/api/v1/items.py - Route Handlers

from fastapi import APIRouter, HTTPException, status

from app.schemas.item import ItemCreate, ItemResponse, ItemUpdate

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

# In-memory storage (sẽ thay bằng database sau)
fake_db: dict[int, dict] = {}
counter = 0


@router.get("/", response_model=list[ItemResponse])
async def list_items(skip: int = 0, limit: int = 10):
    """Lấy danh sách items với pagination."""
    items = list(fake_db.values())
    return items[skip : skip + limit]


@router.get("/{item_id}", response_model=ItemResponse)
async def get_item(item_id: int):
    """Lấy thông tin item theo ID."""
    if item_id not in fake_db:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Item with id {item_id} not found",
        )
    return fake_db[item_id]


@router.post("/", response_model=ItemResponse, status_code=status.HTTP_201_CREATED)
async def create_item(item: ItemCreate):
    """Tạo item mới."""
    global counter
    counter += 1
    item_data = {"id": counter, **item.model_dump()}
    fake_db[counter] = item_data
    return item_data


@router.put("/{item_id}", response_model=ItemResponse)
async def update_item(item_id: int, item: ItemUpdate):
    """Cập nhật item theo ID."""
    if item_id not in fake_db:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Item with id {item_id} not found",
        )
    stored_item = fake_db[item_id]
    update_data = item.model_dump(exclude_unset=True)
    stored_item.update(update_data)
    fake_db[item_id] = stored_item
    return stored_item


@router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int):
    """Xóa item theo ID."""
    if item_id not in fake_db:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Item with id {item_id} not found",
        )
    del fake_db[item_id]

app/main.py - Entry Point

from fastapi import FastAPI

from app.api.v1.items import router as items_router
from app.config import settings

app = FastAPI(
    title=settings.app_name,
    version=settings.app_version,
    description="FastAPI Tutorial - Learning from basic to advanced",
    docs_url="/docs",       # Swagger UI
    redoc_url="/redoc",     # ReDoc
    openapi_url="/openapi.json",
)

# Include routers
app.include_router(items_router, prefix="/api/v1")


@app.get("/")
async def root():
    return {
        "app": settings.app_name,
        "version": settings.app_version,
        "docs": "/docs",
    }


@app.get("/health")
async def health_check():
    return {"status": "healthy"}

5. Chạy Development Server

# Chạy với uv
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

# Hoặc chạy trực tiếp
uvicorn app.main:app --reload

# Output:
# INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
# INFO:     Started reloader process [12345] using WatchFiles
# INFO:     Started server process [12346]
# INFO:     Waiting for application startup.
# INFO:     Application startup complete.

Uvicorn Options quan trọng

# Development
uvicorn app.main:app \
    --reload \              # Auto-reload khi code thay đổi
    --host 0.0.0.0 \       # Listen all interfaces
    --port 8000 \           # Port
    --log-level debug       # Log level

# Production (preview)
uvicorn app.main:app \
    --workers 4 \           # Số worker processes
    --host 0.0.0.0 \
    --port 8000 \
    --no-access-log         # Tắt access log cho performance

6. Khám phá Swagger UI & ReDoc

Sau khi chạy server, truy cập:

  • Swagger UI: http://localhost:8000/docs - Interactive API documentation
  • ReDoc: http://localhost:8000/redoc - Alternative documentation
  • OpenAPI JSON: http://localhost:8000/openapi.json - Raw OpenAPI spec

Swagger UI cho phép:

  • Xem tất cả API endpoints với HTTP methods
  • Xem request/response schemas
  • "Try it out" - gọi API trực tiếp từ browser
  • Xem example values và validation rules

7. Test API với curl

# Health check
curl http://localhost:8000/health

# Tạo item
curl -X POST http://localhost:8000/api/v1/items/ \
  -H "Content-Type: application/json" \
  -d '{"name": "Laptop", "description": "Gaming laptop", "price": 1299.99}'

# Lấy danh sách items
curl http://localhost:8000/api/v1/items/

# Lấy item theo ID
curl http://localhost:8000/api/v1/items/1

# Cập nhật item
curl -X PUT http://localhost:8000/api/v1/items/1 \
  -H "Content-Type: application/json" \
  -d '{"price": 999.99}'

# Xóa item
curl -X DELETE http://localhost:8000/api/v1/items/1

8. VS Code Setup cho FastAPI

Cài đặt extensions cần thiết:

// .vscode/extensions.json
{
  "recommendations": [
    "ms-python.python",
    "ms-python.vscode-pylance",
    "charliermarsh.ruff",
    "ms-python.mypy-type-checker"
  ]
}
// .vscode/settings.json
{
  "python.defaultInterpreterPath": ".venv/bin/python",
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.organizeImports": "explicit"
    }
  }
}

Tổng kết

Trong bài này, chúng ta đã:

  • Cài đặt Python, uv và FastAPI
  • Khởi tạo project với cấu trúc thư mục chuẩn
  • Viết ứng dụng CRUD API đầu tiên với Pydantic schemas
  • Chạy development server và khám phá Swagger UI
  • Cấu hình VS Code cho Python development

Bài tiếp theo sẽ đi sâu vào Path Operations, Request và Response handling trong FastAPI.