Backend from Scratch · Python REST API

FastAPI × SQLite で作る、
いちばん小さなバックエンド

「バックエンド」と聞くと身構える。けれど正体は単純だ——誰かのリクエストを受け取り、データを出し入れして、返す。 Python の FastAPI と、ファイル1つで動くデータベース SQLite があれば、 サーバもクラウドも要らず、手元のPCで「動くAPI」が今日作れる。この一枚で、その全体像を掴む。

1. Request

受け取る

ブラウザやアプリからの HTTP リクエスト

2. FastAPI

捌く

URLごとに Python 関数へ振り分け

3. SQLite

永続化する

1ファイルの DB に読み書き

4. Response

返す

結果を JSON にして応答

01 — なぜ この2つ か

サーバもクラウドも、まだ要らない

最初のバックエンドに「本格的な構成」は不要。学習の摩擦を最小にする2つを選ぶ。

FastAPI — Python の Web フレームワーク。関数の上に @app.get("/…") と一行書くだけで、その URL が API になる。 型ヒントを書けば入力チェックと API ドキュメントが自動生成される。速く、学びやすく、いま最も勢いがある。

SQLite — サーバ不要のデータベース。Python に最初から同梱されていて、インストールも起動もいらない。 データは app.db という1個のファイルに溜まる。プログラムを止めても消えない=これが「永続化」。

この2つが噛み合う理由。 FastAPI が「入口」、SQLite が「保管庫」。あいだを Python で繋ぐだけで、 ファイル1つ・追加サーバゼロの完結したバックエンドになる。増えてきたら PostgreSQL 等へ差し替えればよく、書き方はほぼ変わらない。
02 — 3分ハンズオン

「動くAPI」を、まず起動する

理屈より先に動かす。ターミナルで3ステップ。Python が入っていれば数分で /docs が開く。

Step 1 — 入れる
# 仮想環境を作って有効化(推奨)
python3 -m venv venv
source venv/bin/activate   # Windows は venv\Scripts\activate

# FastAPI 本体と、開発サーバ uvicorn を入れる
pip install "fastapi[standard]"
Step 2 — 書く(main.py)
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "こんにちは、バックエンド"}

@app.get("/hello/{name}")
def hello(name: str):
    return {"hello": name}
* URL の {name} はそのまま関数の引数に届く。: str が型ヒント。
Step 3 — 起動して、触る
fastapi dev main.py
→ ブラウザで http://127.0.0.1:8000 を開くと JSON が返る。
いちばんの感動ポイント: http://127.0.0.1:8000/docs を開くと、触れる API ドキュメント(Swagger UI)が自動で完成している。 型ヒントから生成されたもので、その場でリクエストを送って動作確認できる。
03 — REST の文法

4つの動詞で、データを操る

REST API は「何を(URL)」×「どうする(HTTPメソッド)」で表す。 データの基本操作 CRUD(作成・読取・更新・削除)が、4つの動詞にきれいに対応する。

メソッド意味 (CRUD)URL の例裏で走る SQL
POSTCreate 作成/itemsINSERT
GETRead 読取/items · /items/3SELECT
PUTUpdate 更新/items/3UPDATE
DELETEDelete 削除/items/3DELETE

↓ 下のシミュレータで、動詞を選ぶと リクエスト → FastAPI のコード → 走る SQL → 返る JSON がまとめて見える。 バックエンドの中で「同じ流れ」が繰り返されているだけ、というのが体で分かる。

① FastAPI のコード


      

② 走る SQL


        

③ 返る JSON


      
04 — 永続化の実装

SQLite に、本当に書き込む

上のシミュレータの「裏側」を最小コードで。Python 標準の sqlite3 だけで、テーブル作成から CRUD まで一通り動く。

起動時に テーブルを用意
import sqlite3
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

def db():
    # 1ファイル app.db に繋ぐ。無ければ自動で作られる
    con = sqlite3.connect("app.db")
    con.row_factory = sqlite3.Row   # 列名でアクセスできる
    return con

# 起動時に一度だけ:テーブルが無ければ作る
with db() as con:
    con.execute("""CREATE TABLE IF NOT EXISTS items(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        done  INTEGER DEFAULT 0)""")
入力の形を決める(Pydantic)
# POST で受け取る JSON の形。型に反したら FastAPI が自動で 422 を返す
class ItemIn(BaseModel):
    title: str
    done: bool = False
* この一枚で「入力バリデーション」が完成する。手書きの if 文は要らない。
CRUD の実体(4動詞そろい踏み)
# CREATE
@app.post("/items")
def create(item: ItemIn):
    with db() as con:
        cur = con.execute(
            "INSERT INTO items(title, done) VALUES(?, ?)",
            (item.title, int(item.done)))
        return {"id": cur.lastrowid, "title": item.title, "done": item.done}

# READ(一覧)
@app.get("/items")
def list_items():
    with db() as con:
        rows = con.execute("SELECT * FROM items").fetchall()
        return [dict(r) for r in rows]

# UPDATE
@app.put("/items/{item_id}")
def update(item_id: int, item: ItemIn):
    with db() as con:
        cur = con.execute(
            "UPDATE items SET title=?, done=? WHERE id=?",
            (item.title, int(item.done), item_id))
        if cur.rowcount == 0:
            raise HTTPException(404, "見つかりません")
    return {"id": item_id, "title": item.title, "done": item.done}

# DELETE
@app.delete("/items/{item_id}")
def delete(item_id: int):
    with db() as con:
        con.execute("DELETE FROM items WHERE id=?", (item_id,))
    return {"deleted": item_id}
ここだけは守る(セキュリティの一丁目一番地)。 SQL に値を差し込むときは必ず ? プレースホルダを使い、 文字列連結(f"… {title}")で組み立てない。連結すると SQL インジェクション——外部からの入力で DB を乗っ取られる典型的な穴——ができる。 上のコードが全部 ?(値,) のタプルで渡しているのはそのため。
05 — つまずきポイント

最初に必ず引っかかる3つ

① データが消える? → 消えていない。 SQLite は app.db ファイルに書く。サーバを再起動しても残る。 逆に「消したい」ときはこのファイルを消すだけ。.gitignoreapp.db を入れて、DBファイルはコミットしないのが定石。

② ブラウザから叩けない(CORS)。 別ドメインのフロントから呼ぶと弾かれることがある。 その時は CORSMiddleware を1ブロック足す。まずは /docs から試せば CORS は関係ないので、切り分けが楽。

③ 本番で SQLite のまま? → 個人・小規模なら十分。 同時書き込みが激しくなったら PostgreSQL 等へ。 sqlite3SQLAlchemy(ORM)に替えておくと、その乗り換えが設定1行で済む。次の学習の入口はここ。

06 — 次の一歩

公式ドキュメントへ、そのまま進む

この一枚は「地図」。実際に手を動かす番地は、本家の Tutorial が世界一よくできている(日本語訳あり)。

もくもく会での進め方の提案。 ①この一枚を読む → ②Step 1〜3 を自分のPCで起動 → ③/docs で触る → ④第4章の CRUD を写経 → ⑤公式 Tutorial へ。ここまでで「自分専用の ToDo API」が手元に残る。AIエージェントに繋ぐ土台にもなる。