0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flask入門 — FastAPIと何が違うのか比べた

0
Posted at

はじめに

FastAPIをある程度触ったので、次はFlaskを触ってみた。

PythonのWebフレームワークを調べると必ず名前が出てくるのがFlask。「マイクロフレームワーク」という言葉が気になっていたので、FastAPIとどう違うのかを中心に整理した。


インストールと最初のアプリ

pip install flask
# app.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello, World!"

@app.route("/users/<int:user_id>")
def get_user(user_id):
    return {"user_id": user_id, "name": "田中"}
flask run
# または
python app.py

デコレータでルートを定義する書き方はFastAPIとそっくり。ただしFastAPIが@app.get()と HTTPメソッドまで指定するのに対して、Flaskは@app.route()でパスだけ定義するのがデフォルト。

# Flaskでメソッドを指定する場合
@app.route("/users", methods=["GET", "POST"])
def users():
    ...

FastAPIと並べて比較する

同じAPIをFastAPIとFlaskで書き比べてみた。

ルーティング

# FastAPI
from fastapi import FastAPI
app = FastAPI()

@app.get("/users/{user_id}")
def get_user(user_id: int):
    return {"user_id": user_id}
# Flask
from flask import Flask
app = Flask(__name__)

@app.route("/users/<int:user_id>")
def get_user(user_id):
    return {"user_id": user_id}

パスパラメータの書き方が違う。Flaskは<int:user_id>という独自の構文、FastAPIは{user_id}でPythonの型ヒントと組み合わせる。

JSONレスポンス

# FastAPI → dictを返すだけで自動でJSONになる
@app.get("/users")
def list_users():
    return [{"id": 1, "name": "田中"}]
# Flask → jsonifyが必要(Flask 2.2以降はdictも自動変換)
from flask import jsonify

@app.route("/users")
def list_users():
    return jsonify([{"id": 1, "name": "田中"}])

# Flask 2.2以降はこれでも動く
@app.route("/users")
def list_users():
    return [{"id": 1, "name": "田中"}]

リクエストボディの取得

# FastAPI → Pydanticモデルで型安全に受け取る
from pydantic import BaseModel

class UserCreate(BaseModel):
    name:  str
    email: str

@app.post("/users")
def create_user(user: UserCreate):
    return {"name": user.name}
# Flask → requestオブジェクトから手動で取り出す
from flask import request

@app.route("/users", methods=["POST"])
def create_user():
    data = request.get_json()
    name  = data.get("name")
    email = data.get("email")
    return {"name": name}

ここが一番大きな違い。FastAPIはPydanticによるバリデーションが標準で入っているが、Flaskはrequest.get_json()で辞書を取り出して自分で処理する必要がある。バリデーションが必要ならmarshmallowflask-pydanticなどを別途入れる。


Flaskのrequestオブジェクト

Flaskはリクエストの情報をrequestグローバルオブジェクトから取り出す。

from flask import request

@app.route("/search")
def search():
    # クエリパラメータ
    keyword  = request.args.get("keyword", "")
    page     = request.args.get("page", 1, type=int)

    # リクエストボディ(JSON)
    data = request.get_json()

    # フォームデータ
    name = request.form.get("name")

    # ヘッダー
    token = request.headers.get("Authorization")

    # メソッド
    print(request.method)  # GET / POST など

    return {"keyword": keyword, "page": page}

FastAPIは引数に型ヒントを書けば自動で解決されるが、Flaskはrequestオブジェクトを自分で操作する必要がある。PHPの$_GET/$_POST/$_SERVERを自分で触る感覚に近い。


レスポンスのカスタマイズ

from flask import jsonify, make_response

# ステータスコードを指定
@app.route("/users", methods=["POST"])
def create_user():
    return jsonify({"message": "作成しました"}), 201

# ヘッダーを追加
@app.route("/data")
def get_data():
    response = make_response(jsonify({"data": "..."}))
    response.headers["X-Custom-Header"] = "value"
    return response

FastAPIのstatus_code=201はデコレータで指定するが、Flaskはタプルで返す。


エラーハンドリング

from flask import jsonify

@app.errorhandler(404)
def not_found(e):
    return jsonify({"error": "見つかりません"}), 404

@app.errorhandler(500)
def server_error(e):
    return jsonify({"error": "サーバーエラー"}), 500

# 独自例外
class DomainError(Exception):
    def __init__(self, message: str, code: int = 400):
        self.message = message
        self.code    = code

@app.errorhandler(DomainError)
def handle_domain_error(e):
    return jsonify({"error": e.message}), e.code

@app.route("/items/<int:item_id>")
def get_item(item_id):
    if item_id < 0:
        raise DomainError("IDは0以上を指定してください")
    return {"item_id": item_id}

@app.errorhandler()デコレータでエラーハンドラーを登録するのはFastAPIの@app.exception_handler()とほぼ同じ感覚。


Blueprintでルートを分割する

アプリが大きくなったらBlueprintでファイルを分割する。FastAPIのAPIRouterに相当する。

myapp/
├── app.py
└── blueprints/
    ├── __init__.py
    ├── users.py
    └── items.py
# blueprints/users.py
from flask import Blueprint

users_bp = Blueprint("users", __name__, url_prefix="/users")

@users_bp.route("/")
def list_users():
    return [{"id": 1, "name": "田中"}]

@users_bp.route("/<int:user_id>")
def get_user(user_id):
    return {"user_id": user_id}
# app.py
from flask              import Flask
from blueprints.users   import users_bp
from blueprints.items   import items_bp

app = Flask(__name__)
app.register_blueprint(users_bp)
app.register_blueprint(items_bp)

FastAPIのinclude_router()に相当するのがregister_blueprint()url_prefixでプレフィックスを設定するのも同じ。


Flask-SQLAlchemy — ORMの組み込み

FlaskにはデフォルトでORMがないのでFlask-SQLAlchemyを使うのが一般的。

pip install flask-sqlalchemy
from flask           import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///mydb.db"
db = SQLAlchemy(app)

class User(db.Model):
    id    = db.Column(db.Integer, primary_key=True)
    name  = db.Column(db.String(50), nullable=False)
    email = db.Column(db.String(100), unique=True, nullable=False)

# テーブル作成
with app.app_context():
    db.create_all()

@app.route("/users")
def list_users():
    users = User.query.all()
    return [{"id": u.id, "name": u.name} for u in users]

PHPのEloquentに近い書き方。ただしFastAPIでよく使うSQLAlchemyの書き方とは少し異なる(Flask-SQLAlchemyはFlaskのapp contextと統合されている)。


Flaskのテスト

import pytest
from app import app

@pytest.fixture
def client():
    app.config["TESTING"] = True
    with app.test_client() as client:
        yield client

def test_index(client):
    response = client.get("/")
    assert response.status_code == 200

def test_create_user(client):
    response = client.post(
        "/users",
        json={"name": "田中", "email": "tanaka@example.com"},
    )
    assert response.status_code == 201
    assert response.get_json()["name"] == "田中"

app.test_client()でテスト用のHTTPクライアントが作れる。FastAPIのTestClient(starlette)と同じ感覚で使える。


FastAPIとFlaskの比較まとめ

項目 Flask FastAPI
リリース 2010年 2018年
型ヒント活用 オプション 中心設計
バリデーション 別途ライブラリが必要 Pydantic標準
ドキュメント自動生成 別途設定が必要 /docsで自動
非同期サポート Flask 2.0以降(限定的) ネイティブ対応
リクエスト取得 requestグローバル 引数で受け取る
ORM Flask-SQLAlchemy SQLAlchemy(直接)
学習コスト 低い やや高い
エコシステム 成熟・拡張が豊富 成長中

どちらを選ぶか

触ってみた感想として:

Flaskが向いていると思う場面

  • シンプルなWebアプリ・管理画面(HTMLテンプレート含む)
  • 既存のFlaskプロジェクトのメンテナンス
  • 学習コストを低くしたいとき

FastAPIが向いていると思う場面

  • 型安全なAPIを最初から作りたいとき
  • Swagger UIの自動生成が必要なとき
  • 非同期処理が必要なとき

Flaskは「シンプルな分だけ自由」という設計で、必要なものを自分で選んで組み合わせる。FastAPIは「型ヒントを書けば多くのことが自動化される」という設計。どちらが優れているというより、用途と好みで選ぶ感じ。

自分は新規のAPI開発はFastAPIで書いて、Flaskは既存コードを読むときや軽いスクリプトに使う程度にしようと思っている。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?