😃

[Python]Fast APIでTodoリストをつくってみた

2021/12/15に公開

はじめに

表題の通りですが、FastAPIでTODOリストをつくってみました。

もともと「FastAPIのドキュメント自動生成機能がいいらしいぞ?」と聞いて興味を持ち、Pythonチュートリアル→FastAPIチュートリアル→こちらのZenn本(FastAPI入門)と経由して、今回自分なりに構築してみた次第です。

[バックエンド]
https://github.com/momonoki1990/fastapi-todo-list-api
[フロントエンド]
https://github.com/momonoki1990/fastapi-todo-list-frontend

FastAPIを試してみたかっただけなので本当は必要なかったんですが、ついでにフロントエンドも作ってみました笑
やっぱりフロントもあった方が作った感が出てテンション上がりますね(最後におまけとして少しだけ記載)

※(2022/01/06)FastAPIの方に認証機能をつけました([Python]Fast APIでTodoリストに認証機能をつけてみた)が、フロントエンドは追従していないため、認証エラーとなります(つまりほぼ動かない😢)

※動作にはDockerが必要になります。起動方法は各リポジトリのREADMEをご参照ください。

ドキュメント自動生成機能

サーバーを起動して/docsにアクセスすると、APIドキュメントにアクセスすることができます。
リクエストやレスポンスの形式がリソースごとに一覧できて、「Try it out」というボタンを押すと、実際にリクエストを実行することができます(これって、どんなレスポンスが返ってくるかシュミレートできるだけだと思っていたのですが、実際にAPI叩いてるんですね...!)

このドキュメントの生成自体は、APIのルーティングを形式に沿った形で記載するだけで行われます。

/src/routers/task.py
@router.get("", response_model=List[task_schema.TaskResponse])
async def list_tasks(db: AsyncSession = Depends(get_db)):
    return await task_crud.get_tasks(db)

例えば上記のコードで、response_model=List[task_schema.TaskResponse]とすることで、レスポンスボディの型を設定しています。

/src/schema/task.py
from pydantic import BaseModel, Field

class TaskResponse(TaskBase):
    id: int = Field(..., gt=0, example=1)
    done: bool = Field(False, description="done task or not")

    class Config:
        orm_mode = True

task_schema.TaskResponseはPydanticのBaseModelを継承したクラスで、メンバー変数にpythonの型ヒントを設定します。
また、Filedというインスタンスにデフォルト値やキーワード引数としてバリデーションやパラメータの説明を追加することができ、これがドキュメントに反映されます。

orm_mode = Trueを設定することで、ORM(今回はSQLAlchemy)のモデルオブジェクトをPydanticのレスポンスオブジェクトに変換してくれるようです。
設定しないとバリデーションエラーになります。

(Schemaは各リソースの下に記載)

私自身PythonでAPIを作ったのが初めてだったのですが、直感的に記述ができて、細かいバリデーションもキーワード引数として値を渡すだけで設定でき、かつドキュメントが自動で生成されるので、開発していて気持ちが良かったです。

(公式のドキュメントも見やすく、一部日本語に翻訳されている)

SQLAlchemy

今回SQLAlchemyをORMとして使ってみましたが、最初は公式ドキュメントのどこを読めばいいかわからず、だいぶ面食らいましたね...

(バージョン選んで入ったところ, Top→Reference→Version1.4)

そもそもSQLAlchemy ORMとSQL Alchemy Coreの2種類があって、後者はSQLを直接書き下すのでしょうか?(よくわかっていない)(ならドライバだけでも良さそうな...)

チュートリアルではORMとCoreが混じって書かれていて最初はどっちのことが書いてあるのかよくわからず、しかも、ORMの方でのクエリの書き方も、2.0 styleと1.x styleがあるらしく、それもどっちのことが書いてあるのか区分けがよくわからず混乱しました。

(公式のGlosaryとDeepL先生の翻訳)

ただ、一度、ORMとCore、1.4 styleと2.0styleがあって、1.4ではfuture=Trueとすることで2.0 styleのクエリが書けることが認識できるとドキュメントのどこを見ればいいかわかりますし、engineっていうのはDBのパスなど設定を渡したもので、sessionengineという設定を保持したものなんだなぁとかなんとなく理解できました(浅いw)(それにしてもDBもっとわかるようになりたいなぁ...)

あと、pythonはasync・await構文(asyncio)が比較的新しいものらしく(私はnode.jsからきました)、対応しているライブラリとそうでないものがあるみたいなのですが、aiomysqlという非同期処理に対応したMySQLドライバはSQLAlchemyでは非推奨で、asyncmyというものが推奨されています。

(aiomysqlは更新が止まっているらしく、リポジトリを見てみると、最新のリリースが2020年11月)

(バージョンごとのトップページの一番下にDialect Documentationという項目があり、そこに各DBごとの設定やドライバに関して記載されている)

Poetry, black, tox, テスト用のDBコンテナ

こちら(【2021】モダンなPython開発環境の紹介)でも紹介されているパッケージマネージャーのPoery(主な参考としたZenn本でも紹介・使用されています)と、blackというフォーマッターを使ってみました。

テスト管理ツールであるtoxではデフォルトのパッケージマネージャーがpython標準のpipとなっているため、下記のようにinstall_commandを設定して各テスト環境の依存ライブラリをpoetryを使用してインストールするようにしました。

tox.ini
[tox]
skipsdist = true
envlist = flake8, py310
install_command = poetry install -D {packages}

[testenv]
deps = pytest
allowlist_externals = poetry
commands =
    poetry install
    poetry run pytest tests/

[testenv:flake8]
deps = flake8
commands =
    poetry run flake8 src/

blackは設定レスなところが好きです。
(max-line-lengthに関しては一悶着あるみたい)

上記Pythonの開発ツールとは関係ないですが、今回テスト環境のDBはテスト用のDockerコンテナを用意しました。

(ボリュームで永続化していないこと以外は開発用のDBコンテナとほぼ一緒)

docker-compose.yml
db-test:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: "todo"
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      TZ: "Asia/Tokyo"
    volumes:
      - ./my.conf:/etc/mysql/conf.d/my.cnf
    ports:
      - 33050:3306

終わり

「作ってみたぞ!」と言いたいばかりに取り止めもなく記事を書きましたが、この辺で終わりにしようかと思います。

ついでに: フロントエンド, Vite, Chakra UI

  • React公式?のcreate-react-appより早くていいらしいということで、Viteというセットアップツールを使ってみました。確かにテンプレートが作成されるまでが早かったし、CRAだとディレクトリやファイルがもっと多くなかったでしたっけ?(試そうと思ったら固まってしまった。最近ローカルのnpmの調子が悪い気がする...)

Create react app vs Vite

Viteで作られたディレクトリ

最近開発者プレビューとして公開されたShopifyのHydrogenというフレームワークでもViteが使われているようです。

  • Chakra UI
    「何やらTailwind的なCSSの書き方ができるらしいぞ」と小耳に挟んで気になっていたので試してみました。
    下記のgridGappy(padding-top,bottom)のように、Reactのpropsとして、全てのCSSを記述することができるようで、個人的にはTailwind CSS同様、クラス名に頭を悩ますこともなさそうだし、直感的で好きです。
    横並びや縦並びのflexコンテナを作るVStack HStackとかも便利でした。
App.tsx
<div className="App">
      <Container>
        <VStack gridGap="8" py="16">
          <Heading as="h1" size="4xl">
            My Todo List
          </Heading>
          <InputForm tasks={tasks} setTasks={setTasks} />
          <List tasks={tasks} setTasks={setTasks} />
        </VStack>
      </Container>
    </div>

Stackをどんどん使おう(リストレイアウト編)

メルカリShopsでも採用されているとのことです。
(メルカリShopsはアプリ上で動いていますが、Next.jsでweb viewとして構築されているらしい)
メルカリShops のフロントエンド

Discussion