FastAPI×MongoDBを用いたAPI開発の手ほどき① 〜環境構築〜
はじめに
こんにちは。LIRIS株式会社で取締役兼エンジニアをしています田村です。
4年ほどFastAPIを使ったAPI開発をしています。
昔はFlaskを扱っていましたが、FastAPIは軽量でAPIドキュメントを自動生成してくれるらしいということで乗り換えました。記法もFlaskに似ているため、Flask経験者はとっつきやすいと思います。
また、DBはPostgreSQLを使っていましたが、複雑な構造のデータ処理をすることが増え、パフォーマンス重視になってきたことからNoSQLのMongoDBを使うことにしました。
FastAPI×MongoDBで開発するようになり、パフォーマンスが向上、SQLAlchemyなどを利用する必要がなくなったため非常に開発しやすくなりました。
ここではFastAPIを扱う中で、最初に知っておいた方が良かったなと思うところや、初心者の方が躓きやすい部分の解決方法をまとめていこうと思います。
環境構築からWeb APIの開発まで、誰かの「手ほどき」になれば幸いです。
本稿では、今後の開発のための環境構築を一から解説していきます。
(FastAPIの公式からDockerイメージが配布されていますが、自分好みの環境を作ったりすることも大事だと思いますので、Dockerファイルから作っていきます。)
コードだけ見たいという方はこちらから取得してください。
想定読者
- PythonでWeb API開発したい、されている方
- FastAPIに興味のある方、数年使っている方
- パフォーマンス向上に興味のある方
FastAPIの環境構築
Dockerのインストール
すでにDockerがPCにインストールされている場合、この工程は不要です。
お手持ちのPCのOSに合ったDockerをインストールしてください。
プロジェクト用のフォルダ作成、ファイル作成
今回は「fastapi-mongo-example」というフォルダを作成しました。
作成したフォルダ内に以下のようにフォルダとファイルを作成します。ファイルの中身はこの後記述していきます。
fastapi-mongo-example/
├ app/
│ ├ .dockerignore
│ └ Dockerfile
├ .gitignore
└ docker-compose.yml
.gitignoreの記述
.gitignoreファイルに以下の記述をします。
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# mac
.DS_Store
# 開発用DB
mongo_dev/
.dockerignoreの記述
.dockerignoreファイルに以下の記述をします。
# git
.git
.gitignore
# Docker
Dockerfile
.dockerignore
# Python
__pycache__
# others
README.md
.DS_Store
Dockerfileの記述
先程作成したDockerfileに以下の記述をします。後ほど追記があります。
このDockerfileは、FastAPIが動作する環境を構築するための内容が記載されます。
FROM python:3.11.1-slim-bullseye
WORKDIR /app
COPY ./ ./
RUN apt-get update \
&& apt-get upgrade -y \
&& pip install -U pip \
&& pip install poetry \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENV PYTHONPATH=/app
ENV LANG ja_JP.UTF-8
docker-compose.ymlの記述
docker-compose.ymlには以下の記述をします。後ほど追記があります。
docker-compose.ymlは、複数のDockerコンテナを管理するための設定ファイルになります。今回は以下3つのコンテナを管理します。
- fastapi: FastAPI
- mongo: MongoDB
- mongoexpress: MongoDBのGUIツール
docker-compose.yml
version: "3.8"
services:
fastapi:
image: "fastapi:${TAG-latest}"
links:
- mongo
build:
context: ./app
dockerfile: Dockerfile
ports:
- 8000:8000
volumes:
- ./app:/app:cached
tty: true
restart: unless-stopped
mongo:
image: mongo:5.0
ports:
- 27017:27017
environment:
MONGO_INITDB_DATABASE: app
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password
volumes:
- ./mongo_dev:/data/db
tty: true
restart: unless-stopped
mongoexpress:
image: mongo-express:0.54
links:
- mongo
ports:
- 8081:8081
environment:
VCAP_APP_HOST: 0.0.0.0
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: password
restart: unless-stopped
※ 本番環境のパスワードなどの機密情報は、絶対ベタ書きしないようにしましょう。今回はローカルだけの想定ですので雑な上ベタ書きです。
Dockerイメージのビルド
ターミナルを開き、作成したfastapi-mongo-exampleフォルダ内で docker-compose build
を実行。
Dockerコンテナの起動
Dockerイメージができあがったので、Dockerコンテナを起動します。ターミナルで docker-compose up -d
を実行します。以下のように表示されればOKです。
$ docker-compose up -d
[+] Building 0.0s (0/0)
[+] Running 4/4
✔ Network fastapi-mongo-example_default Created
✔ Container fastapi-mongo-example-mongo-1 Started
✔ Container fastapi-mongo-example-fastapi-1 Started
✔ Container fastapi-mongo-example-mongoexpress-1 Started
コンテナ内にアクセス
fastapiコンテナにアクセスします。ターミナルで docker-compose exec fastapi /bin/bash
を実行します。
以下のように表示されればOKです。
$ docker-compose exec fastapi /bin/bash
root@851397f2ad9e:/app#
必要ライブラリのインストール
今回はPoetryを用いてPythonライブラリをインストールします。
pipが好みという方はpipで大丈夫です。
Poetryの初期設定
poetry init
- Package name[app]: → Enter
- Version[0.1.0]: → Enter
- Description[]: → Enter
- Author[None, n to skip]: → n → Enter
- License[]: → Enter
- Compatible Python versions[^3.11]: → Enter
- Would you like to define your main dependencies interactively? (yes/no)[yes]→ no → Enter
- Would you like to define your development dependencies interactively? (yes/no)[yes]→ no → Enter
- Do you confirm generation? (yes/no)[yes]→ Enter
上記を実行するとappフォルダ内に"pyproject.toml"ファイルが作成されます。
Poetryを用いて必要なライブラリをインストール
ここでの注意点としては pydantic[email]
のように[]が付いているものはダブルクォーテーションで括ってください。
poetry add fastapi uvicorn pymongo pydantic-settings "pydantic[email]" httpx python-multipart
開発の時だけ使うライブラリは以下のようにインストールします。
poetry add --group dev isort black flake8
これでFastAPIを使えるようになります。
FastAPI × uvicornの起動
main.pyの作成
FastAPIを動かすためにメインのコードを書いていきます。
appフォルダ内に main.py
ファイルを作成し、以下のように記述します。
from fastapi import FastAPI
app = FastAPI(title="FastAPI Sample")
@app.get("/", status_code=200)
def root():
return "成功!"
ファイルを保存できたら、ターミナルで root@01234
のように表示されていることを確認し、以下のコマンドを入力します。
poetry run uvicorn --host 0.0.0.0 --port 8000 main:app
# 以下のような表示になればOK
INFO: Started server process [58]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
最後の main:app
の部分は、main
が実行ファイル名、app
はmain.pyに記述した app = FastAPI(title="FastAPI Sample")
部分のappを指しています。名称を変えたい場合はこのあたり気をつけてください。
起動確認
ここまでうまくできていればFastAPIのサーバが起動できているはずです。
起動確認のため、Chromeなどのブラウザを開き、http://localhost:8000/
にアクセスしてみましょう。画面に"成功!"と表示されていれば問題ありません。
また、デフォルトでSwaggerUIによるAPIドキュメントが自動生成されていますので、http://localhost:8000/docs
にアクセスしてみるとAPIドキュメントが見られます。Swaggerですので実際にAPIをテストできます。
起動確認ができたらCtrl+Cでuvicornを停止します。また、ターミナルに root@000000
のように表示されていれば、exit
と入力しEnterキーでDockerコンテナから抜け出しましょう。
シェルスクリプトの作成
毎回poetry runうんたらかんたらと書くのは面倒なので起動用のシェルスクリプトを作成します。
appフォルダ内に start.sh
ファイルを作成し、以下記述します。
start.sh
#! /usr/bin/env sh
set -e
HOST=${HOST:-0.0.0.0}
PORT=${PORT:-8000}
LOG_LEVEL=${LOG_LEVEL:-info}
LOGCONFIG=${LOGCONFIG:-"./logging.conf"}
WORKERS=${WORKERS:-1}
APP_MODULE=${APP_MODULE:-"main:app"}
exec poetry run uvicorn \
--reload \
--host $HOST \
--port $PORT \
--log-level $LOG_LEVEL \
--log-config $LOGCONFIG \
--workers $WORKERS \
"$APP_MODULE"
logging.confの作成
Docker Desktopやターミナルで docker-compose logs fastapi
のコマンドを実行すると、ログが表示されます。
しかし、uvicornのデフォルトログ形式だとエンドポイントの実行時間が含まれていなかったりしますので、ログのフォーマットを変更します。
appフォルダ内に logging.conf
ファイルを作成し、以下記述します。
(お好みでカスタマイズしていただいて大丈夫です。)
logging.conf
[loggers]
keys=root
[handlers]
keys=stderrHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=INFO
handlers=stderrHandler
[handler_stderrHandler]
class=StreamHandler
formatter=simpleFormatter
args=(sys.stderr,)
propagate=False
[formatter_simpleFormatter]
format=[%(asctime)s][%(levelname)s][%(process)d](%(filename)s:%(lineno)s) %(message)s
Dockerfileの修正
Dockerfileを以下のように修正します。
RUNの中にpoetry installコマンドを追記したのと、最後に1行追記しています。
Dockerfile
FROM python:3.11.1-slim-bullseye
WORKDIR /app
COPY ./ ./
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install wget -y \
&& pip install -U pip \
&& pip install poetry \
&& poetry run pip install -U pip setuptools \
&& poetry install \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENV PYTHONPATH=/app
ENV LANG ja_JP.UTF-8
CMD [ "./start.sh" ]
docker-compose.ymlの修正
環境変数を追加します。
(mongoとmongoexpressは変更なしです。)
docker-compose.yml
version: "3.8"
services:
fastapi:
image: "fastapi:${TAG-latest}"
links:
- mongo
build:
context: ./app
dockerfile: Dockerfile
ports:
- 8000:8000
volumes:
- ./app:/app:cached
environment:
HOST: 0.0.0.0
PORT: 8000
LOG_LEVEL: info
LOGCONFIG: ./logging.conf
WORKERS: 1
APP_MODULE: main:app
tty: true
restart: unless-stopped
Dockerイメージの再構築
Dockerfile、docker-compose.ymlを修正したら、ターミナルで docker-compose build
を実行し、Dockerイメージを再構築します。
再構築できたか確認するために、docker-compose up -d
でDockerコンテナを起動します。
先程と同様に、ブラウザで http://localhost:8000/docs
にアクセスし、APIドキュメントが見られればOKです。
まとめ
長くなってしまいましたので、今回はここまでとします。
今回実施したこと
- Dockerを用いたFastAPI環境構築
- docker-compose.ymlの設定
- FastAPIの起動確認
ここまでのコードはこちらから取得できます。
次回はFastAPIとMongoDBを接続し、サンプルコードを書きながら動作を見ていこうと思います。
この記事を読んでみて何か学びが得られれば幸いです。
Discussion