はじめに
FastAPIはPythonのWEBフレームワークで、同じ分類のフレームワークとしてFlaskやDjangoなどが挙げられます。FlaskとDjangoはよく比較され、Flaskが最小限のコードで書き始められるシンプルなマイクロフレームワークと呼ばれるのに対し、Djangoはログイン機能、データベース管理などが初めから備わっておりフルスタックフレームワークと呼ばれています。
FastAPIはFlaskと似ているマイクロフレームワークで公式ドキュメントでは次のように紹介されています。FastAPI は、Pythonの標準である型ヒントに基づいてPython 3.6 以降でAPI を構築するためのモダンで高速(高パフォーマンス)な、Web フレームワークです。[1]
本記事では、以下の内容を扱います。
- 特徴である型ヒントとそれにより実現できるAPIドキュメントの自動生成
- SQLAlchemyを用いたデータベース操作
- alembicを用いたデータベースマイグレーション
以上の内容を、GitHubが提供するクラウド開発環境Codesapceで行います。GitHubアカウントがあれば無料で、クレジットカード登録不要です。内容は違いますが、Codesapceについては以下の記事で扱っています。
開発環境の起動
CodesapceのPython環境テンプレートにアクセスします。そしてUser this templateからOpen in a codespaceを選択します。
すると以下の様にブラウザでVSCode互換の環境が起動します。詳細は解説しませんが、今回の記事で扱う内容はこの環境で実行することができます。
個人的にダークテーマに変更しています。以降、記事のスクリーンショットはダークテーマとなっています。
FastAPIのインストール
Codesapceの下部にあるターミナルで以下のコマンドを実行します。以降、コマンドの実行はこちらで同様に実行していきます。
pip install "fastapi[all]"
FastAPI hello world
既にあるapp.pyファイルを編集します。
コードは以下になります。
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000, log_level="debug")
FastAPIを起動します。
python3 app.py
すると右下にポップアップが表示されるのでブラウザで開くを選択します。
ブラウザの別タブで動作を確認できます。
データベースの利用
サンプルアプリとしてToDoアプリを扱います。
必要なパッケージをインストール。
pip install alembic SQLAlchemy
データベースの管理情報を初期化します。
alembic init migrations
今回はMySQLなどのデータベースサーバなどを用いずに、単一ファイルでデータベースを扱えるSqliteを利用します。alembic.ini
のsqlalchemy.url
を以下の様に編集します。
sqlalchemy.url = sqlite:///sample.sqlite
settings.py
を作成しデータベースの接続情報などを記載します。
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = 'sqlite:///sample.sqlite'
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
models.py
を作成し、データベースのテーブルを定義します。id
は数値でプライマリーキーとし、title
は文字列型としています。
from sqlalchemy import Column, Integer, String, DateTime
from datetime import datetime
from settings import Base
class TodoModel(Base):
__tablename__ = 'todo'
id = Column(Integer, primary_key=True)
title = Column(String)
created_date = Column(DateTime, default=datetime.utcnow)
migrations/env.py
を以下のように編集し、定義したモデルをAlembicに伝えます。
1 既にあるimport文の下に以下を追加する。
from settings import Base
from models import *
2 target_metadata
グローバル変数を以下のように編集する。
target_metadata = Base.metadata
Pythonコードで定義したモデルを元に、テーブルやカラムをデータベースに作成するコードを生成します。
alembic revision --autogenerate -m "create todo table"
実行すると、migrations/versions/XXXXXXXXXXXX_create_todo_table.py
が生成されます。以下の様に、テーブルを作成し、カラムを追加するコードが確認できます。
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('todo',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('title', sa.String(), nullable=True),
sa.Column('created_date', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
以下のコマンドを実行してデータベースに上記のコードを適用させます。
alembic upgrade head
エンドポイントの作成
app.pyを以下の様に編集します。それぞれどのような処理を行っているかはコメントに記載しています。
from fastapi import FastAPI, Depends
import uvicorn
from schemas import PostTodo
from models import TodoModel
from settings import SessionLocal
from sqlalchemy.orm import Session
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/")
async def root():
return {"message": "Hello World"}
# データベースからToDo一覧を取得するAPI
@app.get("/todo")
def get_todo(
db: Session = Depends(get_db)
):
# query関数でmodels.pyで定義したモデルを指定し、.all()関数ですべてのレコードを取得
return db.query(TodoModel).all()
# ToDoを作成するAPI
@app.post("/todo")
def post_todo(
todo: PostTodo,
db: Session = Depends(get_db)
):
# 受け取ったtitleからモデルを作成
db_model = TodoModel(title = todo.title)
# データベースに登録(インサート)
db.add(db_model)
# 変更内容を確定
db.commit()
return {"message": "success"}
# ToDoを削除するAPI
@app.delete("/todo/{id}")
def delete_todo(
id: int,
db: Session = Depends(get_db)
):
delete_todo = db.query(TodoModel).filter(TodoModel.id==id).one()
db.delete(delete_todo)
db.commit()
return {"message": "success"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000, log_level="debug")
ブラウザからデータを受け取る型を定義するためschemas.py
を作成します。
from typing import List, Optional
from pydantic import BaseModel
class PostTodo(BaseModel):
title: str
動作確認とAPIドキュメントの自動生成
FastAPIを起動します。
python3 app.py
ブラウザを開き、https://○○○○.app.github.dev/docs
にアクセスします。するとFastAPIの特徴である自動生成されたAPIドキュメントを開くことができます。
このページでは簡単にAPIを試すことができます。ToDoを作成してみます。Request body内に、予め用意されてるサンプルデータを書き換え、ToDoのタイトルを記入します。ExceuteをクリックするとAPIを実行できます。
作成したToDoを取得してみます。GETメソッドの/todoを実行します。先ほど作成したタイトルが取得できていることを確認できます。
ToDoの削除に関しても確認します。idを指定してToDoを削除できます。
おわりに
FastAPIを用いて以下の機能を利用しました。これらの機能は様々なプロジェクトで利用するかと思います。
- 特徴である型ヒントとそれにより実現できるAPIドキュメントの自動生成
- SQLAlchemyを用いたデータベース操作
- alembicを用いたデータベースマイグレーション
本記事で作成したファイル群をテンプレートとして活用するとMock作成など素早く実施できるかと思います。特に自動生成されたAPIドキュメントは、WEBダッシュボードを提供し、APIを試すことができるため、開発効率の向上も期待できます。
参考
- [1] tiangolo, FastAPI, https://fastapi.tiangolo.com/ja/