# 

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

&gt; ⏱️ **閱讀時間：** 15 分鐘
&gt; 🎯 **難度：** ⭐ (入門)

---

## 🎯 本章目標

完成這一章後，你將能：
- ✅ 設定 FastAPI 開發環境
- ✅ 建立並運行第一個 API
- ✅ 理解 FastAPI 應用的基本結構
- ✅ 使用 Swagger UI 測試 API

---

## 🔧 環境準備

### 1. 確認 Python 版本

FastAPI 需要 **Python 3.8&#43;**，建議使用 **3.10&#43;**

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

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

### 2. 建立專案目錄

```bash
# 建立專案目錄
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
```

**確認虛擬環境啟動成功：**

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

### 3. 安裝 FastAPI

```bash
# 安裝 FastAPI（包含所有功能）
pip install &#34;fastapi[standard]&#34;

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

**驗證安裝：**

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

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

---

## 🚀 第一個 FastAPI 應用

### 建立 main.py

```python
# main.py
from fastapi import FastAPI

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

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

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

### 啟動伺服器

```bash
# 開發模式啟動（自動重載）
uvicorn main:app --reload

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

**成功啟動後會看到：**

```
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL&#43;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/docs | Swagger UI 文件 |
| http://127.0.0.1:8000/redoc | ReDoc 文件 |

---

## 📖 程式碼詳解

### 導入 FastAPI

```python
from fastapi import FastAPI
```

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

### 建立應用實例

```python
app = FastAPI()
```

**可以加入更多設定：**

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

### 路由裝飾器

```python
@app.get(&#34;/&#34;)
async def root():
    return {&#34;message&#34;: &#34;Hello, FastAPI!&#34;}
```

**解析：**

| 部分 | 說明 |
|------|------|
| `@app.get` | HTTP GET 方法 |
| `&#34;/&#34;` | URL 路徑 |
| `async def` | 非同步函數（也可用 `def`） |
| `root` | 函數名稱（會出現在文件中） |
| `return {...}` | 回傳 JSON |

### 支援的 HTTP 方法

```python
@app.get(&#34;/items&#34;)       # 讀取資源
@app.post(&#34;/items&#34;)      # 建立資源
@app.put(&#34;/items/{id}&#34;)  # 更新資源（完整）
@app.patch(&#34;/items/{id}&#34;)  # 更新資源（部分）
@app.delete(&#34;/items/{id}&#34;)  # 刪除資源
@app.options(&#34;/items&#34;)   # 獲取支援的方法
@app.head(&#34;/items&#34;)      # 獲取 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

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

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

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

# 資料模型
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(&#34;/&#34;)
async def root():
    &#34;&#34;&#34;歡迎訊息&#34;&#34;&#34;
    return {&#34;message&#34;: &#34;歡迎使用待辦事項 API！請訪問 /docs 查看文件&#34;}

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

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

@app.post(&#34;/todos&#34;, response_model=Todo, status_code=201)
async def create_todo(todo: TodoCreate):
    &#34;&#34;&#34;建立新的待辦事項&#34;&#34;&#34;
    global todo_id_counter
    todo_id_counter &#43;= 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(&#34;/todos/{todo_id}&#34;, response_model=Todo)
async def update_todo(todo_id: int, todo: TodoCreate):
    &#34;&#34;&#34;更新待辦事項&#34;&#34;&#34;
    if todo_id not in todos_db:
        raise HTTPException(status_code=404, detail=&#34;待辦事項不存在&#34;)

    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(&#34;/todos/{todo_id}&#34;, status_code=204)
async def delete_todo(todo_id: int):
    &#34;&#34;&#34;刪除待辦事項&#34;&#34;&#34;
    if todo_id not in todos_db:
        raise HTTPException(status_code=404, detail=&#34;待辦事項不存在&#34;)
    del todos_db[todo_id]
```

### 測試流程

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

# 2. 使用 curl 測試（或用 Swagger UI）

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

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

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

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

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

# 刪除待辦事項
curl -X DELETE &#34;http://127.0.0.1:8000/todos/1&#34;
```

---

## 🔄 同步 vs 非同步

FastAPI 同時支援兩種寫法：

### 非同步函數（推薦）

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

**適合：**
- I/O 操作（資料庫、API 呼叫、檔案讀寫）
- 高併發場景

### 同步函數

```python
@app.get(&#34;/sync&#34;)
def sync_endpoint():
    # 一般的同步操作
    data = compute_something()
    return data
```

**適合：**
- CPU 密集運算
- 使用不支援非同步的函式庫

### 選擇建議

```
┌─────────────────────────────────────────────┐
│  你的操作是什麼類型？                        │
├─────────────────────────────────────────────┤
│                                             │
│  I/O 操作（資料庫、HTTP、檔案）              │
│    → 使用 async def &#43; await                 │
│                                             │
│  CPU 密集運算（計算、處理）                  │
│    → 使用 def                               │
│                                             │
│  混合（兩者都有）                            │
│    → 使用 async def，CPU 部分用 run_in_executor │
│                                             │
└─────────────────────────────────────────────┘
```

---

## ⚙️ Uvicorn 進階配置

### 常用啟動參數

```bash
# 基本開發模式
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 啟動

```python
# main.py
from fastapi import FastAPI

app = FastAPI()

@app.get(&#34;/&#34;)
async def root():
    return {&#34;message&#34;: &#34;Hello!&#34;}

# 直接執行此檔案時啟動伺服器
if __name__ == &#34;__main__&#34;:
    import uvicorn
    uvicorn.run(
        &#34;main:app&#34;,
        host=&#34;0.0.0.0&#34;,
        port=8000,
        reload=True,  # 開發模式
        log_level=&#34;info&#34;
    )
```

```bash
# 現在可以直接執行
python main.py
```

---

## 📁 建議的檔案結構

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

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

**requirements.txt:**

```
fastapi[standard]&gt;=0.109.0
```

**.gitignore:**

```
venv/
__pycache__/
*.pyc
.env
.DS_Store
```

---

## ✅ 重點總結

### 環境設定

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

# 2. 安裝 FastAPI
pip install &#34;fastapi[standard]&#34;

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

### 基本結構

```python
from fastapi import FastAPI

app = FastAPI()

@app.get(&#34;/&#34;)
async def root():
    return {&#34;message&#34;: &#34;Hello!&#34;}
```

### 重要 URL

| URL | 說明 |
|-----|------|
| `http://127.0.0.1:8000` | API 根路徑 |
| `http://127.0.0.1:8000/docs` | Swagger UI |
| `http://127.0.0.1:8000/redoc` | ReDoc |

---

## 🎤 面試這樣答

### Q: 如何啟動 FastAPI 應用？

**答案：**

&gt; FastAPI 應用需要 ASGI 伺服器來運行。開發時常用 Uvicorn：
&gt;
&gt; ```bash
&gt; uvicorn main:app --reload
&gt; ```
&gt;
&gt; 生產環境則會用 Gunicorn 配合 Uvicorn workers，或直接用 Uvicorn 配合多個 workers：
&gt;
&gt; ```bash
&gt; gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker
&gt; ```

---

## 🤓 小測驗

1. FastAPI 需要什麼伺服器來運行？
   &lt;details&gt;
   &lt;summary&gt;點擊看答案&lt;/summary&gt;
   ASGI 伺服器，常用 Uvicorn 或 Hypercorn
   &lt;/details&gt;

2. `@app.get(&#34;/&#34;)` 中的 `get` 代表什麼？
   &lt;details&gt;
   &lt;summary&gt;點擊看答案&lt;/summary&gt;
   HTTP GET 方法
   &lt;/details&gt;

3. Swagger UI 的預設路徑是什麼？
   &lt;details&gt;
   &lt;summary&gt;點擊看答案&lt;/summary&gt;
   /docs
   &lt;/details&gt;

4. `--reload` 參數的作用是什麼？
   &lt;details&gt;
   &lt;summary&gt;點擊看答案&lt;/summary&gt;
   程式碼修改時自動重新啟動伺服器（開發模式用）
   &lt;/details&gt;

---

## 🏋️ 練習作業

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

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

---

**上一篇：** [01-1. FastAPI 是什麼？為什麼選擇它？](./01-1)
**下一篇：** [01-3. 路徑參數與查詢參數](./01-3)

---

最後更新：2025-12-17


---

> 作者: luk  
> URL: https://yoru-karu-blog-lalaluk-52581ac5e0cef170a3c8922c19182ecb6f7bd604.gitlab.io/posts/tutorial/fastapi/01-2/  

