PYTHON / POSTGRESQL

在日本伺服器部署 FastAPI 與 PostgreSQL

從 GitHub 部署、資料庫連線到資料保存確認

公開會儲存資料的 FastAPI 應用程式時,除了執行 API 的伺服器,也要準備資料的保存位置。使用 PostgreSQL 的架構中,即使公開網址可以連線,應用程式若無法連上資料庫,仍然不能儲存資料。若把資料放在應用程式的暫存檔案中,重新部署時也可能遺失。

本文會建立一個儲存與列出短筆記的 API,依序確認啟動、資料庫連線與資料保存。部署範例採用東京區域的 PaaS KamuiDash。重點是把相依套件安裝、資料表初始化與 API 啟動分開處理。

可以下載完整範例。本文假設讀者具備基本的 Python 與 Git 操作知識。跟著操作時,請使用簡短的測試筆記,不要放入真實個人資料。

本文內容

分開考慮 API 的執行位置與資料保存位置

FastAPI 負責處理請求,PostgreSQL 負責儲存筆記。瀏覽器或 API 用戶端不會直接連線到資料庫。

資料保存的路徑 · 概念圖
用戶端透過 HTTPS 與驗證標頭存取 API
FastAPI驗證輸入、執行 SQL
PostgreSQL持久保存筆記
資料庫連線資訊留在伺服器端,不直接交給用戶端。

程式中的變數會隨著程序結束而消失。KamuiDash 應用程式的磁碟也是暫存空間;需要跨重啟、跨部署保留的資料應存入資料庫,上傳圖片等檔案則使用物件儲存。也請參閱官方規格中的暫存磁碟說明。

這裡只使用一個應用程式、一個程序的小型範例。範例提供初次建立資料表的 SQL,但不包含追蹤既有結構變更的機制。持續新增功能時,應改用 Alembic 等工具管理資料庫遷移。

準備最小的 FastAPI 範例

下載的資料夾包含以下檔案。本文假設將資料夾內的內容作為 GitHub 儲存庫根目錄。

Text
requirements.txt   Python 相依套件
main.py            API 與啟動處理
init_db.py         初次建立資料表
.gitignore         排除 .env 等本機檔案

在 requirements.txt 固定相依套件版本

Text
fastapi==0.128.8
uvicorn==0.39.0
psycopg[binary]==3.2.13

FastAPI 提供 API 框架,Uvicorn 執行 HTTP 伺服器,Psycopg 用於連線 PostgreSQL。本文所列的組合已在本機確認。日後更新版本時,請在變更後重新確認寫入與讀取。

使用 PORT 啟動,並為資料 API 加上驗證

Python
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

Python
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 中建立範例專用的空資料庫。以下連線位置與使用者名稱只是範例,這項確認不需要使用外部正式環境的資料庫。

Terminal
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,再執行:

Terminal
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 等連線資訊會透過環境變數設定,不必直接複製本機使用的網址。

KAMUIDASH · 畫面截圖
KamuiDash 應用程式建立畫面,可選擇執行語言與 GitHub 儲存庫。
建立應用程式時的執行環境選擇畫面。FastAPI 請選擇 Web server 下的 Python。圖片沿用既有公開文章中的實際畫面。
項目 此範例的設定 需要確認的事
類型與語言 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 使用與本機不同且足夠長的值

若將範例放在既有儲存庫的子資料夾,請把該資料夾設為根目錄。指令中的路徑都是相對於所選目錄。

部署時的三個階段 · 概念圖
安裝依 requirements.txt 準備套件
啟動前在專用資料庫準備資料表
啟動使用 PORT 監聽 HTTP
初始建立指令碼不能取代結構變更歷程管理。

公開後,分別確認網址、資料保存與更新

部署成功後,對平台提供的 HTTPS 網址,依照本機相同順序確認:先 /healthz,再 /readyz,接著建立筆記與列出筆記。

然後保留一筆筆記,小幅修改原始碼並重新部署,再確認仍能讀到同一筆資料。這能檢查 API 程序被替換後,資料是否仍保存在資料庫中。

KAMUIDASH · 畫面截圖
KamuiDash 靜態網站詳細資訊:執行狀態、公開網址、GitHub 分支與各次提交的部署紀錄。
應用程式詳細畫面的範例。這是既有靜態網站的畫面,用來說明如何對照公開網址與部署紀錄,並不是本範例 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 應用程式。