FastAPIを用いたAPI開発テンプレート

## はじめに

**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については以下の記事で扱っています。

https://qiita.com/abek21/items/4f128ab442e4e23fb9cc

## 開発環境の起動

[CodesapceのPython環境テンプレート](https://github.com/microsoft/vscode-remote-try-python)にアクセスします。そして**User this template**から**Open in  a codespace**を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/a9f445e8-92a2-8e80-bd03-5f3aff975136.png)

すると以下の様にブラウザでVSCode互換の環境が起動します。詳細は解説しませんが、今回の記事で扱う内容はこの環境で実行することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/c8e54851-722e-cde2-4826-0f42a1e7d67e.png)

個人的にダークテーマに変更しています。以降、記事のスクリーンショットはダークテーマとなっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/9f0716be-7ec2-37ae-abac-6a70a2368b3b.png)

## FastAPIのインストール

Codesapceの下部にあるターミナルで以下のコマンドを実行します。以降、コマンドの実行はこちらで同様に実行していきます。

```bash
pip install "fastapi[all]"
```

## FastAPI hello world

既にあるapp.pyファイルを編集します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/13fa6552-8c7d-8966-5c22-737fa7462791.png)

コードは以下になります。

```python
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を起動します。

```python
python3 app.py
```

すると右下にポップアップが表示されるので**ブラウザで開く**を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/21a23209-7657-0aa9-c9eb-6086b9575692.png)

ブラウザの別タブで動作を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/5c059821-2e91-162a-4e1d-bda4ee930df4.png)

## データベースの利用

サンプルアプリとしてToDoアプリを扱います。

必要なパッケージをインストール。

```bash
pip install alembic SQLAlchemy
```

データベースの管理情報を初期化します。

```
alembic init migrations
```

今回はMySQLなどのデータベースサーバなどを用いずに、単一ファイルでデータベースを扱えるSqliteを利用します。`alembic.ini`の`sqlalchemy.url`を以下の様に編集します。


```
sqlalchemy.url = sqlite:///sample.sqlite
```

`settings.py`を作成しデータベースの接続情報などを記載します。

```python
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`は文字列型としています。

```python
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文の下に以下を追加する。

```python
from settings import Base
from models import *
```

2 `target_metadata`グローバル変数を以下のように編集する。

```python
target_metadata = Base.metadata
```


Pythonコードで定義したモデルを元に、テーブルやカラムをデータベースに作成するコードを生成します。

```
alembic revision --autogenerate -m "create todo table"
```

実行すると、`migrations/versions/XXXXXXXXXXXX_create_todo_table.py`が生成されます。以下の様に、テーブルを作成し、カラムを追加するコードが確認できます。

```python
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')
    )
```

以下のコマンドを実行してデータベースに上記のコードを適用させます。

```bash
alembic upgrade head
```


## エンドポイントの作成

app.pyを以下の様に編集します。それぞれどのような処理を行っているかはコメントに記載しています。

```python
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`を作成します。

```python
from typing import List, Optional
from pydantic import BaseModel


class PostTodo(BaseModel):
    title: str
```

## 動作確認とAPIドキュメントの自動生成

FastAPIを起動します。

```bash
python3 app.py 
```

ブラウザを開き、`https://○○○○.app.github.dev/docs`にアクセスします。するとFastAPIの特徴である自動生成されたAPIドキュメントを開くことができます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/6375de8b-b8fc-bff7-feda-c1b873c853b6.png)

このページでは簡単にAPIを試すことができます。ToDoを作成してみます。**Request body**内に、予め用意されてるサンプルデータを書き換え、ToDoのタイトルを記入します。**Exceute**をクリックするとAPIを実行できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/5bb826a5-4428-1ff3-8f05-d5f4f20d447a.png)


作成したToDoを取得してみます。GETメソッドの/todoを実行します。先ほど作成したタイトルが取得できていることを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/fcf6b750-4438-4c4b-0437-5c5f120217bc.png)

ToDoの削除に関しても確認します。idを指定してToDoを削除できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/ecfdf764-9482-cfd6-a84d-fd39cddb6d66.png)



## おわりに

FastAPIを用いて以下の機能を利用しました。これらの機能は様々なプロジェクトで利用するかと思います。

- 特徴である型ヒントとそれにより実現できるAPIドキュメントの自動生成
- SQLAlchemyを用いたデータベース操作
- alembicを用いたデータベースマイグレーション

本記事で作成したファイル群をテンプレートとして活用するとMock作成など素早く実施できるかと思います。特に自動生成されたAPIドキュメントは、WEBダッシュボードを提供し、APIを試すことができるため、開発効率の向上も期待できます。

## 参考

- [1] tiangolo, FastAPI, https://fastapi.tiangolo.com/ja/