自己紹介
こんにちは,ZOZOテクノロジーズの内定者さっとです。
最近FastAPIをよく利用していますが、今回紹介するGraphQLやWebSocketがサポートされており、
便利すぎでは?と感じる今日この頃です。
※本記事はZOZOテクノロジーズ#5の17日目です.
昨日は@e_tyuboさんが「Git for Windowsでautocrlfの設定を間違えちゃった時の対応」という記事を出しました.
他にもZOZOテクノロジーズでは5つのアドベントカレンダーを毎日更新しています!
概要
FastAPIを使ってGraphQLをさくっと構築する方法を紹介したいと思います。
以下、本記事の構成です。
- FastAPI?GraphQLとは?
- FastAPI×GraphQLで楽々ハローワールド
- SQLAlchemyを使ってDB接続
FastAPIとは
Python3.6以上で動作する高速なAPIフレームワークです。
https://fastapi.tiangolo.com/
2019年初頭頃、少し話題になったと思います。
特徴としては、Node.jsやGoと同等のパフォーマンス性能でかつFlaskライクな書き方で簡単にRESTful APIやGraphQL、WebSocketを構築することができます。ハッカソンなど時間が限られた開発で有効なフレームワークだと思います。
また、Swaggerが自動で生成されるため、動作チェックやドキュメント作成の時間が大幅に短縮されます。
GraphQLとは
Facebookによって、従来のRESTful APIの問題点を解決するために開発されたオープンソースの言語です。
https://graphql.org/
特徴としては、1つのエンドポイントを使用して、必要なデータを必要な分だけ1回のクエリで取得できるという点です。
この利点によって、従来のRESTful APIで必要なデータを取得するために、余計なデータまで付属してきたという状況を回避することができます。これにより、電力の消費やネットワークの帯域を抑えた通信が可能となりました。
Hello GraphQL
コードの説明は一旦おいておいて、動かしてみましょう
$ git clone https://github.com/sattosan/hello_fastapi_graphql.git
# コンテナをビルド&起動
$ docker-compose up -d --build
GraphQLの動作チェック
コンテナが起動次第、下記のリンクにアクセスしてみましょう
http://127.0.0.1:8000/graphql
そうすると、下記のようなWebユーザインタフェースが表示されると思います。
画像の通り、下記のクエリを左側に記述して、実行すると
{
hello(name: "FastAPI")
}
右側にレスポンスが表示されることがわかるかと思います。
コードの説明
では、さっそくコードの中身を見ていきましょう。
動作環境
- Ubuntu 18.04.3 および macOS Catalina 10.15.3
- Python 3.8
- FastAPI 0.45.0
ファイル構成
今回は、Pythonのいろいろなパッケージを使う関係で、Docker Composeを使って環境を構築しています。
.
├── api
│ └── main.py
├── docker
│ └── uvicorn
│ ├── Dockerfile
│ └── requirements.txt
└── docker-compose.yml
Docker Composeで環境構築
- Composeファイル
version: "3"
services:
# FastAPI
api:
container_name: "api"
build: ./docker/uvicorn
restart: always
tty: true
ports:
- 8000:8000
volumes:
- ./api:/usr/src/api
- FastAPIのコンテナを定義
FROM python:3.8
WORKDIR /usr/src/api
ADD requirements.txt ./
# requirements.txtにリストされたパッケージをインストールする
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# FastAPIを8000ポートで待機
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
- 必要なPythonのパッケージを一気にインストールするためにリスト化したもの
uvicorn
fastapi
graphene
FastAPIでGraphQLのエンドポイント作成
RESTful APIと違い、GraphQLで扱うエンドポイントは1つだけです。
今回は、FastAPIを使用して、エンドポイント/graphql
上にGraphQLスキーマを公開するサーバと、クエリを簡単に実行するためのGraphiQLと呼ばれるWebユーザインタフェースを作成します。
ライブラリstarletteを使うことで、これらの機能を実現するためのサポートをしてくれます。
import graphene
from fastapi import FastAPI
from starlette.graphql import GraphQLApp
# Grapheneを利用したGraphQLスキーマを作成する
class Query(graphene.ObjectType):
# 引数nameを持つフィールドhelloを作成
hello = graphene.String(name=graphene.String(default_value="stranger"))
# フィールドhelloに対するユーザへ返すクエリレスポンスを定義
def resolve_hello(self, info, name):
return "Hello " + name
# FastAPIを利用するためのインスタンスを作成
app = FastAPI()
# GraphQLのエンドポイント
app.add_route("/graphql", GraphQLApp(schema=graphene.Schema(query=Query)))
SQLAlchemyを使ってDB接続
DBを用意して、GraphQLでデータを取得できるように既存ファイルを編集します。
DBはSQLiteで構築し、ORMモジュールであるSQLAlchemyを使ってデータを操作します。
完成コードはGitHubにあるので、すぐ試したい方は、こちらにアクセスしてください。
ファイルの追加&編集
変更後のファイル構成は、このようになっています。
.
├── api
│ ├── main.py # 編集
│ ├── models.py # 追加
│ ├── schema.py # 追加
│ └── init_data.py # 追加
├── docker
│ └── uvicorn
│ ├── Dockerfile
│ └── requirements.txt # 編集
└── docker-compose.yml
パッケージの追加
FastAPIのコンテナでDBとデータのやり取りを行うために、
SQLAlchemyとGraphQLを扱うためのgraphene_sqlalchemyを追加します。
uvicorn
fastapi
graphene
SQLAlchemy # 追加
graphene_sqlalchemy # 追加
テーブルモデルの定義
SQLiteと通信するためのdb_sessionを作成し、
DepartmentとEmployeeテーブルを定義します。
# flask_sqlalchemy/models.py
from sqlalchemy import *
from sqlalchemy.orm import (scoped_session, sessionmaker, relationship,
backref)
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///database.sqlite3', convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
# クエリを扱うために宣言
Base.query = db_session.query_property()
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
name = Column(String)
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String)
hired_on = Column(DateTime, default=func.now())
department_id = Column(Integer, ForeignKey('department.id'))
department = relationship(
Department,
backref=backref('employees',
uselist=True,
cascade='delete,all'))
スキーマーの定義
特定の従業員情報や部署情報を取得するためのEmployeeとDepartmentを定義します。
また、Queryとして、特定のノードを取得するためのnodeと、
すべての従業員情報や部署情報を取得するための、all_employees、all_departmentを提供しています。
import graphene
from graphene import relay
from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
from models import Department as DepartmentModel, Employee as EmployeeModel
class Department(SQLAlchemyObjectType):
class Meta:
model = DepartmentModel
interfaces = (relay.Node, )
class DepartmentConnections(relay.Connection):
class Meta:
node = Department
class Employee(SQLAlchemyObjectType):
class Meta:
model = EmployeeModel
interfaces = (relay.Node, )
class EmployeeConnections(relay.Connection):
class Meta:
node = Employee
class Query(graphene.ObjectType):
node = relay.Node.Field()
# デフォルトでは、ソートが有効化されている
all_employees = SQLAlchemyConnectionField(EmployeeConnections)
# ソートを無効化
all_departments = SQLAlchemyConnectionField(DepartmentConnections, sort=None)
schema = graphene.Schema(query=Query)
FastAPIのコードを修正
定義したスキーマー及びクエリをGraphQLAppに投げて扱えるようにします。
また、 シャットダウン時に、DBセッションを削除するイベントを追加します。
from fastapi import FastAPI
from starlette.graphql import GraphQLApp
from models import db_session
from schema import schema
# FastAPIのインスタンスを作成
app = FastAPI()
# GraphQLを提供するためのエンドポイントを定義
app.add_route("/graphql", GraphQLApp(schema=schema))
# APIサーバシャットダウン時にDBセッションを削除
@app.on_event("shutdown")
def shutdown_event():
db_session.remove()
テストデータの挿入
SQLite上で作成したテーブルに下記のコマンドを入力して、テストデータを挿入します。
$ docker-compose run --rm api python init_data.py
init_data.pyはテストデータをまとめたものです。
# 初期データを流し込むためのスクリプト.以下を実行してTableにデータを流し込む
# docker-compose run --rm api python init_data.py
from models import engine, db_session, Base, Department, Employee
Base.metadata.create_all(bind=engine)
# Tableに流し込む初期データ
engineering = Department(name='Engineering')
db_session.add(engineering)
hr = Department(name='Human Resources')
db_session.add(hr)
peter = Employee(name='Peter', department=engineering)
db_session.add(peter)
roy = Employee(name='Roy', department=engineering)
db_session.add(roy)
tracy = Employee(name='Tracy', department=hr)
db_session.add(tracy)
db_session.commit()
コンテナの再構築
もろもろ変更したので、コンテナを再構築します。
$ docker-compose up -d --build
DBのデータをGraphQLでリクエストしてみる
下記のリンクにアクセスして、GraphQLでDBに挿入したデータを取得してみましょう。
このようなクエリを投げてみてください。
{
allEmployees {
edges {
node {
id
name
department {
name
}
}
}
}
}
allEmployeesを使って、すべての従業員情報を取得することができました。
まとめ
今回は、FastAPIを使ってGraphQLの構築方法を紹介しました。
たった数行で構築することができ、とりあえず試してみようと思う方にとっては、
FastAPIはベストな選択だと思います。
明日は、@hmsnakrさんの記事です!お楽しみに!!
参考
https://fastapi.tiangolo.com/tutorial/graphql/
https://docs.graphene-python.org/projects/sqlalchemy/en/latest/tutorial/