公開會儲存資料的 FastAPI 應用程式時,除了執行 API 的伺服器,也要準備資料的保存位置。使用 PostgreSQL 的架構中,即使公開網址可以連線,應用程式若無法連上資料庫,仍然不能儲存資料。若把資料放在應用程式的暫存檔案中,重新部署時也可能遺失。
本文會建立一個儲存與列出短筆記的 API,依序確認啟動、資料庫連線與資料保存。部署範例採用東京區域的 PaaS KamuiDash。重點是把相依套件安裝、資料表初始化與 API 啟動分開處理。
可以下載完整範例。本文假設讀者具備基本的 Python 與 Git 操作知識。跟著操作時,請使用簡短的測試筆記,不要放入真實個人資料。
本文內容
分開考慮 API 的執行位置與資料保存位置
FastAPI 負責處理請求,PostgreSQL 負責儲存筆記。瀏覽器或 API 用戶端不會直接連線到資料庫。
程式中的變數會隨著程序結束而消失。KamuiDash 應用程式的磁碟也是暫存空間;需要跨重啟、跨部署保留的資料應存入資料庫,上傳圖片等檔案則使用物件儲存。也請參閱官方規格中的暫存磁碟說明。
這裡只使用一個應用程式、一個程序的小型範例。範例提供初次建立資料表的 SQL,但不包含追蹤既有結構變更的機制。持續新增功能時,應改用 Alembic 等工具管理資料庫遷移。
準備最小的 FastAPI 範例
下載的資料夾包含以下檔案。本文假設將資料夾內的內容作為 GitHub 儲存庫根目錄。
requirements.txt Python 相依套件
main.py API 與啟動處理
init_db.py 初次建立資料表
.gitignore 排除 .env 等本機檔案
在 requirements.txt 固定相依套件版本
fastapi==0.128.8
uvicorn==0.39.0
psycopg[binary]==3.2.13
FastAPI 提供 API 框架,Uvicorn 執行 HTTP 伺服器,Psycopg 用於連線 PostgreSQL。本文所列的組合已在本機確認。日後更新版本時,請在變更後重新確認寫入與讀取。
使用 PORT 啟動,並為資料 API 加上驗證
import hmac
import os
from typing import Optional
import psycopg
import uvicorn
from fastapi import Depends, FastAPI, Header, HTTPException
from pydantic import BaseModel, Field
from psycopg.rows import dict_row
app = FastAPI(title="Small Notes API")
def connection():
# Context managers commit on success, roll back on error, and close.
return psycopg.connect(os.environ["DATABASE_URL"], connect_timeout=5,
row_factory=dict_row)
def authorize(authorization: Optional[str] = Header(default=None)):
token = os.environ.get("DEMO_API_TOKEN", "")
if not token:
raise HTTPException(status_code=503, detail="API token is not configured")
expected = "Bearer " + token
if not hmac.compare_digest((authorization or "").encode(), expected.encode()):
raise HTTPException(status_code=401, detail="Authentication required")
class Note(BaseModel):
body: str = Field(min_length=1, max_length=500)
@app.get("/healthz")
def health():
return {"ok": True}
@app.get("/readyz", dependencies=[Depends(authorize)])
def ready():
with connection() as conn:
conn.execute("SELECT 1")
return {"database": "ok"}
@app.get("/notes", dependencies=[Depends(authorize)])
def list_notes():
with connection() as conn:
return conn.execute("SELECT id, body FROM notes ORDER BY id DESC LIMIT 50").fetchall()
@app.post("/notes", status_code=201, dependencies=[Depends(authorize)])
def create_note(note: Note):
with connection() as conn:
return conn.execute("INSERT INTO notes (body) VALUES (%s) RETURNING id, body",
(note.body,)).fetchone()
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=int(os.getenv("PORT", "8000")))
以 0.0.0.0 監聽,並讀取平台提供的 PORT。若只監聽 127.0.0.1,應用程式外部就無法連線。KamuiDash 的 Python 設定指南也說明了使用 PORT 與明確設定安裝指令的方式。
/healthz 只確認 API 程序是否回應。資料庫連線則由需要驗證的 /readyz 另外確認。如此便能區分問題出在伺服器未啟動,還是資料庫無法使用。
這是使用一組共用權杖的學習用 API,不包含個別使用者帳號與權限管理。若要提供一般使用者存取,請加入符合用途的驗證與請求頻率限制。寫在瀏覽器端 JavaScript 裡的權杖無法保持機密。
分開處理初次建立資料表的 SQL
from main import connection
with connection() as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS notes (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
body text NOT NULL CHECK (length(body) BETWEEN 1 AND 500)
)
""")
print("notes table is ready")
由於使用 IF NOT EXISTS,重複執行指令碼不會刪除既有筆記。但資料表已存在時,也不會更新欄位定義。請從專用的空資料庫開始,不要直接套用到既有資料庫。
在本機確認儲存與讀取
先在自己的本機 PostgreSQL 中建立範例專用的空資料庫。以下連線位置與使用者名稱只是範例,這項確認不需要使用外部正式環境的資料庫。
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export DATABASE_URL='postgresql://demo_user:local_password@127.0.0.1:5432/notes_demo'
export DEMO_API_TOKEN='local-demo-only-change-before-publication'
python init_db.py
python main.py
在另一個終端機設定相同的 DEMO_API_TOKEN,再執行:
curl --fail http://127.0.0.1:8000/healthz
curl --fail http://127.0.0.1:8000/readyz \
-H "Authorization: Bearer $DEMO_API_TOKEN"
curl --fail http://127.0.0.1:8000/notes \
-H "Authorization: Bearer $DEMO_API_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"body":"第一次保存測試"}'
curl --fail http://127.0.0.1:8000/notes \
-H "Authorization: Bearer $DEMO_API_TOKEN"
預期寫入時回傳 HTTP 201,列出筆記時回傳 HTTP 200,JSON 中包含已儲存的文字。移除驗證標頭後存取 /notes,會回傳 401。在不把資料庫網址或權杖寫入日誌的前提下,完成以上確認後再推送到 GitHub。
在 KamuiDash 設定 PostgreSQL 與 Python 應用程式
先在 KamuiDash 專案中建立 PostgreSQL,再於建立應用程式時選擇該資料庫。官方資料庫連線指南說明,DATABASE_URL 等連線資訊會透過環境變數設定,不必直接複製本機使用的網址。

| 項目 | 此範例的設定 | 需要確認的事 |
|---|---|---|
| 類型與語言 | Web server/Python | 不要建立成靜態網站 |
| 原始碼 | 目標 GitHub 儲存庫與分支 | 選擇包含完整範例的提交版本 |
| 根目錄 | 留空 | 假設 main.py 位於儲存庫根目錄 |
| 安裝指令 | pip install -r requirements.txt |
安裝相依套件 |
| 啟動前指令 | python init_db.py |
在專用資料庫準備初始資料表 |
| 啟動指令 | python main.py |
使用 PORT 監聽 |
| 健康檢查 | /healthz |
不需驗證即可回傳 200 |
| 資料庫 | 已建立的 PostgreSQL | 確認 DATABASE_URL 已設定 |
| 額外環境變數 | DEMO_API_TOKEN |
使用與本機不同且足夠長的值 |
若將範例放在既有儲存庫的子資料夾,請把該資料夾設為根目錄。指令中的路徑都是相對於所選目錄。
公開後,分別確認網址、資料保存與更新
部署成功後,對平台提供的 HTTPS 網址,依照本機相同順序確認:先 /healthz,再 /readyz,接著建立筆記與列出筆記。
然後保留一筆筆記,小幅修改原始碼並重新部署,再確認仍能讀到同一筆資料。這能檢查 API 程序被替換後,資料是否仍保存在資料庫中。

無法啟動時,先看哪裡
| 症狀 | 優先確認的項目 |
|---|---|
ModuleNotFoundError |
安裝日誌與 requirements.txt 的位置 |
| 無法連線到公開網址 | 啟動日誌、0.0.0.0、PORT 與健康檢查路徑 |
/healthz 回傳 200,但 /readyz 失敗 |
資料庫狀態與應用程式的資料庫連線設定 |
relation "notes" does not exist |
啟動前指令的結果,以及實際連線的資料庫 |
/notes 回傳 401 |
正式環境的權杖與請求中的驗證標頭 |
| 流量增加後資料庫連線失敗 | 同時連線數、程序數與資料庫方案的連線上限 |
範例會為每個請求開啟連線,操作結束後關閉,並非持續保留連線的方式;不過,同時存取量較大時,仍可能達到連線上限。正式環境應將資料庫上限、程序數與副本數一起納入連線池大小的規劃。調整連線池數量的實驗文章也可作為判斷材料。
常見問題
可以永久免費公開嗎?
KamuiDash 提供符合條件的應用程式與資料庫基本費用 30 天免費試用,持續使用則需付費。個人方案的一個 Nano 應用程式與一個 Nano 資料庫使用 30 天,基本費用合計為未稅 US$9,用量費用等另計。詳細條件請參閱官方價格與規格。
SQLAlchemy 與 Alembic 是必要的嗎?
執行這個小型範例不需要。但開始修改資料表或管理多個環境後,就需要追蹤結構變更的機制,不能只靠 CREATE TABLE IF NOT EXISTS 持續維運。
資料庫在日本,是否代表所有資料只在日本處理?
結合外部驗證、郵件、監控、AI API 等服務時,仍會發生對各服務的資料傳送。應用程式與資料庫在東京執行,和整套系統的資料所在地,是需要分開確認的事。
新增功能前,先保留可驗證資料保存的最小版本
這個版本能用來確認啟動、資料庫連線、寫入、讀取,以及重新部署後讀取同一筆資料。把這個狀態保留在 Git 中,日後新增功能出現問題時,就能比較變更從哪裡開始。
驗證範圍:附件範例的 API 與資料庫操作已在本機確認。KamuiDash 設定依據 2026 年 11 月 2 日確認的官方資料,並未為此範例另行部署新的雲端應用程式進行驗證。
參考資料:啟動 FastAPI 伺服器、Psycopg 連線與交易。
延伸閱讀
KamuiDash 是位於東京區域的 PaaS,可透過 GitHub 與 AI 整合公開 Web 應用程式。