01-2. 環境設定與第一個 API

⏱️ 閱讀時間: 15 分鐘 🎯 難度: ⭐ (入門)


🎯 本章目標

完成這一章後,你將能:

  • ✅ 設定 FastAPI 開發環境
  • ✅ 建立並運行第一個 API
  • ✅ 理解 FastAPI 應用的基本結構
  • ✅ 使用 Swagger UI 測試 API

🔧 環境準備

1. 確認 Python 版本

FastAPI 需要 Python 3.8+,建議使用 3.10+

# 檢查 Python 版本
python --version
# 或
python3 --version

# 輸出應該是 Python 3.8 以上
# Python 3.11.x 或 Python 3.12.x

2. 建立專案目錄

# 建立專案目錄
mkdir fastapi-learning
cd fastapi-learning

# 建立虛擬環境
python -m venv venv

# 啟動虛擬環境
# macOS / Linux
source venv/bin/activate

# Windows (Command Prompt)
venv\Scripts\activate.bat

# Windows (PowerShell)
venv\Scripts\Activate.ps1

確認虛擬環境啟動成功:

# 你的終端機應該顯示 (venv) 前綴
(venv) $ which python
/path/to/fastapi-learning/venv/bin/python

3. 安裝 FastAPI

# 安裝 FastAPI(包含所有功能)
pip install "fastapi[standard]"

# 這會安裝:
# - fastapi: 核心框架
# - uvicorn: ASGI 伺服器
# - pydantic: 資料驗證
# - starlette: Web 框架核心
# - httpx: HTTP 客戶端(測試用)
# - jinja2: 模板引擎
# - python-multipart: 表單支援

驗證安裝:

# 檢查 FastAPI 版本
python -c "import fastapi; print(fastapi.__version__)"
# 輸出: 0.109.0 或更高

# 檢查 Uvicorn
uvicorn --version
# 輸出: uvicorn 0.27.0 或更高

🚀 第一個 FastAPI 應用

建立 main.py

# main.py
from fastapi import FastAPI

# 建立 FastAPI 應用實例
app = FastAPI()

# 定義根路徑
@app.get("/")
async def root():
    return {"message": "Hello, FastAPI!"}

# 定義另一個端點
@app.get("/hello/{name}")
async def say_hello(name: str):
    return {"message": f"Hello, {name}!"}

啟動伺服器

# 開發模式啟動(自動重載)
uvicorn main:app --reload

# 參數說明:
# main: 檔案名稱 (main.py)
# app: FastAPI 實例名稱
# --reload: 程式碼修改時自動重啟

成功啟動後會看到:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to stop)
INFO:     Started reloader process [28720] using WatchFiles
INFO:     Started server process [28722]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

測試 API

打開瀏覽器訪問:

URL說明
http://127.0.0.1:8000根路徑
http://127.0.0.1:8000/hello/John帶參數的路徑
http://127.0.0.1:8000/docsSwagger UI 文件
http://127.0.0.1:8000/redocReDoc 文件

📖 程式碼詳解

導入 FastAPI

from fastapi import FastAPI

這行導入 FastAPI 類別,它是整個框架的核心。

建立應用實例

app = FastAPI()

可以加入更多設定:

app = FastAPI(
    title="我的第一個 API",
    description="這是一個學習用的 API",
    version="0.1.0",
    docs_url="/docs",        # Swagger UI 路徑
    redoc_url="/redoc",      # ReDoc 路徑
    openapi_url="/openapi.json"  # OpenAPI schema
)

路由裝飾器

@app.get("/")
async def root():
    return {"message": "Hello, FastAPI!"}

解析:

部分說明
@app.getHTTP GET 方法
"/"URL 路徑
async def非同步函數(也可用 def
root函數名稱(會出現在文件中)
return {...}回傳 JSON

支援的 HTTP 方法

@app.get("/items")       # 讀取資源
@app.post("/items")      # 建立資源
@app.put("/items/{id}")  # 更新資源(完整)
@app.patch("/items/{id}")  # 更新資源(部分)
@app.delete("/items/{id}")  # 刪除資源
@app.options("/items")   # 獲取支援的方法
@app.head("/items")      # 獲取 headers(無 body)

🎨 Swagger UI 導覽

訪問 http://127.0.0.1:8000/docs

┌──────────────────────────────────────────────┐
│  我的第一個 API                    [Authorize] │
│  這是一個學習用的 API                          │
├──────────────────────────────────────────────┤
│                                              │
│  default                                     │
│  ┌────────────────────────────────────────┐  │
│  │ GET  /     Root                        │  │
│  │      讀取根路徑                         │  │
│  └────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────┐  │
│  │ GET  /hello/{name}  Say Hello          │  │
│  │      向指定名稱打招呼                   │  │
│  └────────────────────────────────────────┘  │
│                                              │
└──────────────────────────────────────────────┘

功能:

  • 📋 查看所有 API 端點
  • 🧪 線上測試 API
  • 📄 查看請求/回應格式
  • 🔐 測試認證功能

📝 完整範例:待辦事項 API

讓我們建立一個稍微複雜的範例:

# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
from datetime import datetime

app = FastAPI(
    title="待辦事項 API",
    description="一個簡單的待辦事項管理 API",
    version="1.0.0"
)

# 資料模型
class TodoCreate(BaseModel):
    title: str
    description: Optional[str] = None
    completed: bool = False

class Todo(BaseModel):
    id: int
    title: str
    description: Optional[str] = None
    completed: bool
    created_at: datetime

# 模擬資料庫
todos_db: dict[int, Todo] = {}
todo_id_counter = 0

# API 端點
@app.get("/")
async def root():
    """歡迎訊息"""
    return {"message": "歡迎使用待辦事項 API!請訪問 /docs 查看文件"}

@app.get("/todos", response_model=list[Todo])
async def get_todos():
    """獲取所有待辦事項"""
    return list(todos_db.values())

@app.get("/todos/{todo_id}", response_model=Todo)
async def get_todo(todo_id: int):
    """獲取單一待辦事項"""
    if todo_id not in todos_db:
        raise HTTPException(status_code=404, detail="待辦事項不存在")
    return todos_db[todo_id]

@app.post("/todos", response_model=Todo, status_code=201)
async def create_todo(todo: TodoCreate):
    """建立新的待辦事項"""
    global todo_id_counter
    todo_id_counter += 1

    new_todo = Todo(
        id=todo_id_counter,
        title=todo.title,
        description=todo.description,
        completed=todo.completed,
        created_at=datetime.now()
    )
    todos_db[todo_id_counter] = new_todo
    return new_todo

@app.put("/todos/{todo_id}", response_model=Todo)
async def update_todo(todo_id: int, todo: TodoCreate):
    """更新待辦事項"""
    if todo_id not in todos_db:
        raise HTTPException(status_code=404, detail="待辦事項不存在")

    existing = todos_db[todo_id]
    updated_todo = Todo(
        id=todo_id,
        title=todo.title,
        description=todo.description,
        completed=todo.completed,
        created_at=existing.created_at
    )
    todos_db[todo_id] = updated_todo
    return updated_todo

@app.delete("/todos/{todo_id}", status_code=204)
async def delete_todo(todo_id: int):
    """刪除待辦事項"""
    if todo_id not in todos_db:
        raise HTTPException(status_code=404, detail="待辦事項不存在")
    del todos_db[todo_id]

測試流程

# 1. 啟動伺服器
uvicorn main:app --reload

# 2. 使用 curl 測試(或用 Swagger UI)

# 建立待辦事項
curl -X POST "http://127.0.0.1:8000/todos" \
     -H "Content-Type: application/json" \
     -d '{"title": "學習 FastAPI", "description": "完成第一章"}'

# 回應:
# {"id":1,"title":"學習 FastAPI","description":"完成第一章","completed":false,"created_at":"2025-12-17T10:30:00"}

# 獲取所有待辦事項
curl "http://127.0.0.1:8000/todos"

# 獲取單一待辦事項
curl "http://127.0.0.1:8000/todos/1"

# 更新待辦事項
curl -X PUT "http://127.0.0.1:8000/todos/1" \
     -H "Content-Type: application/json" \
     -d '{"title": "學習 FastAPI", "completed": true}'

# 刪除待辦事項
curl -X DELETE "http://127.0.0.1:8000/todos/1"

🔄 同步 vs 非同步

FastAPI 同時支援兩種寫法:

非同步函數(推薦)

@app.get("/async")
async def async_endpoint():
    # 使用 await 呼叫非同步操作
    data = await fetch_data_from_db()
    return data

適合:

  • I/O 操作(資料庫、API 呼叫、檔案讀寫)
  • 高併發場景

同步函數

@app.get("/sync")
def sync_endpoint():
    # 一般的同步操作
    data = compute_something()
    return data

適合:

  • CPU 密集運算
  • 使用不支援非同步的函式庫

選擇建議

┌─────────────────────────────────────────────┐
│  你的操作是什麼類型?                        │
├─────────────────────────────────────────────┤
│                                             │
│  I/O 操作(資料庫、HTTP、檔案)              │
│    → 使用 async def + await                 │
│                                             │
│  CPU 密集運算(計算、處理)                  │
│    → 使用 def                               │
│                                             │
│  混合(兩者都有)                            │
│    → 使用 async def,CPU 部分用 run_in_executor │
│                                             │
└─────────────────────────────────────────────┘

⚙️ Uvicorn 進階配置

常用啟動參數

# 基本開發模式
uvicorn main:app --reload

# 指定 host 和 port
uvicorn main:app --host 0.0.0.0 --port 8080

# 指定 workers(生產環境)
uvicorn main:app --workers 4

# 完整配置
uvicorn main:app \
    --host 0.0.0.0 \
    --port 8000 \
    --workers 4 \
    --log-level info \
    --access-log

使用 Python 啟動

# main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello!"}

# 直接執行此檔案時啟動伺服器
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(
        "main:app",
        host="0.0.0.0",
        port=8000,
        reload=True,  # 開發模式
        log_level="info"
    )
# 現在可以直接執行
python main.py

📁 建議的檔案結構

目前只有一個檔案沒關係,但建議從一開始就養成好習慣:

fastapi-learning/
├── venv/                  # 虛擬環境
├── app/
│   ├── __init__.py
│   └── main.py            # 主程式
├── requirements.txt       # 依賴清單
├── .env                   # 環境變數
├── .gitignore
└── README.md

requirements.txt:

fastapi[standard]>=0.109.0

.gitignore:

venv/
__pycache__/
*.pyc
.env
.DS_Store

✅ 重點總結

環境設定

# 1. 建立虛擬環境
python -m venv venv
source venv/bin/activate  # macOS/Linux

# 2. 安裝 FastAPI
pip install "fastapi[standard]"

# 3. 啟動伺服器
uvicorn main:app --reload

基本結構

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello!"}

重要 URL

URL說明
http://127.0.0.1:8000API 根路徑
http://127.0.0.1:8000/docsSwagger UI
http://127.0.0.1:8000/redocReDoc

🎤 面試這樣答

Q: 如何啟動 FastAPI 應用?

答案:

FastAPI 應用需要 ASGI 伺服器來運行。開發時常用 Uvicorn:

uvicorn main:app --reload

生產環境則會用 Gunicorn 配合 Uvicorn workers,或直接用 Uvicorn 配合多個 workers:

gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker

🤓 小測驗

  1. FastAPI 需要什麼伺服器來運行?

  2. @app.get("/") 中的 get 代表什麼?

  3. Swagger UI 的預設路徑是什麼?

  4. --reload 參數的作用是什麼?


🏋️ 練習作業

建立一個簡單的「書籍管理 API」:

  1. GET /books - 獲取所有書籍
  2. GET /books/{id} - 獲取單一書籍
  3. POST /books - 新增書籍(title, author, year)
  4. DELETE /books/{id} - 刪除書籍

上一篇: 01-1. FastAPI 是什麼?為什麼選擇它? 下一篇: 01-3. 路徑參數與查詢參數


最後更新:2025-12-17

0%