PythonのWebフレームワークFastAPIが、2026年10月18日から20日のわずか3日間で、0.144から0.150まで一気に版を重ねました。そのほとんどに、互換性を壊す変更が含まれています。
中心になっているのは、HTMLのフォームから送られてきた値の扱いです。チェックしなかったチェックボックスや、空欄のまま送られた入力欄を、ブラウザの実際の動きに合わせて解釈するように変わりました。
フォームを使ったアプリをFastAPIで作っている方は、上げるだけで結果が変わる可能性があります。今回は、実際に新旧の版で動かしながら、何が変わったのかを見ていきます。
まずは何が変わったのかを整理しよう¶
最初に、3日間で公開された版と、それぞれの主な変更を表にまとめます。内容は、FastAPIの公式リポジトリにあるリリースノートにもとづいています。
| 版 | 公開日 | 主な変更 |
|---|---|---|
| 0.144.0 | 10月18日 | セキュリティ用の依存関係がWebSocketでも使えるように |
| 0.145.0 | 10月18日 | 非推奨だったopenapi_prefixを削除 |
| 0.146.0 | 10月18日 | FastAPI()のroot_path引数を非推奨に |
| 0.147.0 | 10月19日 | 送られてこなかったboolのフォーム項目をFalseとして扱う |
| 0.148.0 | 10月19日 | 省略された項目の既定値を、利用者の入力として扱わないように |
| 0.149.0 | 10月19日 | フォームモデルの空文字を、値なしとして扱う |
| 0.150.0 | 10月20日 | 選ばれなかったファイル入力を、値なしとして扱う |
0.147.0から0.150.0までの4つは、リリースノートでいずれも互換性を壊す変更として扱われています。日付はリリースノートの記載で、PyPIへの公開は日本時間ではずれることがあります。
FastAPIそのものがまだよくわからない方は、先にこちらを読んでおくと話がつながります。【関連記事】PythonのFastAPIとは?
ブラウザのフォームは、実はこう送っている¶
今回の変更を理解するには、ブラウザがフォームの中身をどう送っているかを知っておく必要があります。少し意外な動きをするのです。
| 入力の種類 | 何もしなかったときにブラウザが送るもの |
|---|---|
| チェックボックス | 何も送らない(項目そのものがない) |
| 文字の入力欄 | 空文字("")を送る |
| ファイルの選択 | 名前も中身も空のファイルを送る |
チェックボックスは、チェックしなかったときには項目ごと送られません。一方で文字の入力欄は、空欄でも空文字として必ず送られます。
これまでのFastAPIは、こうしたブラウザの癖をあまり考慮していませんでした。今回の一連の変更は、ブラウザが実際に送ってくる形に合わせるためのものです。
チェックしなかったチェックボックスがFalseに¶
まずは0.147.0の変更から見てみましょう。リリースノートによると、送られてこなかったboolのフォーム項目は、Falseとして扱われるようになりました。
チェックしなかったHTMLのチェックボックスと同じ意味になるように、という説明です。既定値をTrueやNoneにしていても、項目が送られてこなければFalseになります。
新旧の版で同じコードを動かしてみた¶
実際に、会員登録のようなフォームを受け取るAPIを書いて、0.143.5と0.150.0で比べてみました。メールマガジンの購読を表すnewsletterは、既定値をTrueにしています。
from typing import Annotated
from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
app = FastAPI()
@app.post("/signup")
def signup(
name: Annotated[str, Form()],
nickname: Annotated[str, Form()] = "名無し",
newsletter: Annotated[bool, Form()] = True,
):
return {"name": name, "nickname": nickname, "newsletter": newsletter}
client = TestClient(app)
# ブラウザのフォームと同じく、チェックしなかったnewsletterは送られず、
# 空のままのnicknameは空文字として送られる
res = client.post("/signup", data={"name": "taro", "nickname": ""})
print(res.status_code, res.json())
それぞれの版を新しい仮想環境に入れ、手元のPython 3.11で実行した結果です。動作に関係のない警告の行は省いています。
FastAPI 0.143.5
200 {'name': 'taro', 'nickname': '名無し', 'newsletter': True}
FastAPI 0.150.0
200 {'name': 'taro', 'nickname': '名無し', 'newsletter': False}
newsletterの値が、TrueからFalseに変わりました。チェックボックスにチェックを入れなかった利用者が、これまでは購読する扱いになっていたわけです。
エラーは出ずに、結果だけが静かに変わる変更なので、気づかないまま上げると思わぬ影響が出るかもしれません。既定値をTrueにしているboolのフォーム項目がないか、確かめておきましょう。
空欄の入力を、値なしとして扱うように¶
次は0.149.0の変更です。リリースノートによると、フォームモデルの中の空の値を、送られなかったものとして扱うようになりました。
フォームモデルとは、Pydanticのモデルを使って、フォームの項目をまとめて受け取る書き方です。個別のFormの引数では以前からこの扱いだったので、それに合わせた形だと説明されています。
空欄の年齢でエラーにならなくなった¶
こちらも、実際に試してみました。名前と年齢を受け取るフォームモデルで、年齢を空欄のまま送ります。
from typing import Annotated
from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
from pydantic import BaseModel
class Profile(BaseModel):
name: str
age: int | None = None
app = FastAPI()
@app.post("/profile")
def profile(data: Annotated[Profile, Form()]):
return data.model_dump()
client = TestClient(app)
res = client.post("/profile", data={"name": "hanako", "age": ""})
print(res.status_code, res.json())
0.143.5と0.150.0で実行した結果です。0.143.5のエラーの内容は、要点だけに短くしています。
FastAPI 0.143.5
422 ... 'msg': 'Input should be a valid integer, unable to parse string as an integer', 'input': ''
FastAPI 0.150.0
200 {'name': 'hanako', 'age': None}
0.143.5では、空文字を整数として読めずに、422のエラーになりました。0.150.0では、空欄は値なしとして扱われ、既定値のNoneが入っています。
空欄を許したい入力欄で、利用者がエラー画面を見ずに済むようになったのは嬉しい変更です。なお、リリースノートによると、JSONで送られてきたリクエストの扱いは変わっていません。Noneの扱いについては、こちらで紹介しています。【関連記事】PythonのNoneとは?
既定値の扱いと、ファイル入力の変更¶
ほかにも、フォームに関わる変更が2つあります。どちらも、上げる前に知っておきたい内容です。
既定値は検証されなくなった¶
0.148.0では、省略された項目の既定値を、利用者の入力として扱わないようになりました。リリースノートによると、既定値は、validate_default=Trueを指定しない限り検証されません。
既定値に対して検証のための関数が動くことを前提にしていた場合は、Field(validate_default=True)などで明示する必要があります。Pydanticのモデルの検証については、こちらで紹介しています。【関連記事】Pydantic v2とは?
選ばれなかったファイルは値なしに¶
0.150.0では、ファイルを選ばずに送信したときの扱いが変わりました。ブラウザは、名前も中身も空のファイルを送ってきますが、FastAPIはこれを値なしとして扱うようになりました。
リリースノートによると、必須のファイル項目は検証エラーになり、既定値があればそれが使われます。名前の付いた0バイトのファイルは、これまでどおり受け付けられます。
そのほかの変更¶
フォーム以外にも、3日間でいくつかの変更が入っています。代表的なものを紹介します。
0.144.0では、ログインの確認などに使うセキュリティ用の依存関係が、WebSocketでも使えるようになりました。0.146.0では、FastAPI()のroot_path引数が非推奨になり、fastapi run --root-path /api/v1のように、サーバーを起動するときに指定する方法が案内されています。
0.145.0では、以前から非推奨だったopenapi_prefixが削除されました。古いサンプルコードを参考にしている方は、使っていないか確認しておきましょう。
FastAPIは、少し前の0.142.0でもOpenTelemetryへの対応を加えたばかりです。このところ、大きな変更が続いています。【関連記事】FastAPIにOpenTelemetryが標準搭載
上げる前にやっておきたいこと¶
最後に、FastAPIを上げる前に確かめておきたいことを整理します。
| 順番 | 確認すること |
|---|---|
| 1 | 既定値をTrueにしたboolのフォーム項目がないか |
| 2 | 空欄をエラーにすることを前提にしたフォームモデルがないか |
| 3 | 既定値に検証が走ることを前提にしていないか |
| 4 | root_pathやopenapi_prefixを使っていないか |
| 5 | フォームを送るテストを、新しい版で一通り流す |
FastAPIはまだ1.0になっていないライブラリで、0.x系の中でも互換性を壊す変更が入ることがあります。requirements.txtなどで、使う版を固定しておくのが基本です。
私は10年ほどエンジニアとして開発に関わってきましたが、チェックボックスの値の扱いの違いで、利用者の同意の有無を取り違えかけたことがあります。フォームのテストでは、チェックを入れた場合だけでなく、入れなかった場合も必ず確かめるようにしています。
まとめ¶
FastAPIは、2026年10月18日から20日にかけて0.144から0.150まで公開され、HTMLフォームの扱いが大きく変わりました。チェックしなかったチェックボックスはFalseに、空欄や選ばれなかったファイルは値なしとして扱われるようになっています。
ブラウザの実際の動きに合わせた、筋のよい変更です。ただし、エラーを出さずに結果が変わるものもあるので、上げる前にフォームのテストを確かめておきましょう。
ここまでお読みいただきありがとうございました。