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

Lesson 3: Install and Initialize FastAPI Project

Install FastAPI and Uvicorn, initialize project structure, understand standard directory structure. Run the development server, Swagger UI, ReDoc and write the first API endpoint.

💻 Programming — Lesson 3 Lesson 3: Install and Initialize FastAPI Project

Python FastAPI: From Basics to Advanced

Part 1: Python Foundation & FastAPI

xdev.asia

1. Install the development environment

System requirements

  • Python 3.12+ (3.12 or 3.13 recommended)
  • uv or Poetry for dependency management
  • VS Code or PyCharm with Python extensions
  • Git for version control

Install 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

UV settings (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. Initialize 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 after initialization:

[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. Project structure

Create a standard directory structure:

# 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. Write the first FastAPI Application

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. Run 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 important

# 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. Explore Swagger UI & ReDoc

After running the server, access:

  • 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 allows:

  • See all API endpoints with HTTP methods
  • See request/response schemas
  • "Try it out" - call API directly from browser
  • See example values and validation rules

7. Test API with 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 for FastAPI

Install necessary extensions:

// .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"
    }
  }
}

Summary

In this article, we have:

  • Install Python, uv and FastAPI
  • Initialize project with standard directory structure
  • Write your first CRUD API application with Pydantic schemas
  • Run the development server and explore Swagger UI
  • Configure VS Code for Python development

The next article will delve into Path Operations, Request and Response handling in FastAPI.