はじめに
- GraphQLの勉強のため、以下のサイトのfull-stack tutorialをやってみた。
- https://www.apollographql.com/docs/tutorial/introduction/
- サーバー側はなんとか理解したが、クライアント側がReactベースで全く分からない。
- 代わりにPythonからGraphQL Clientを使用してみた。
GraphQLサーバーの起動
cd fullstack-tutorial\final\server
npm run start
http://127.0.0.1:4000/
スキーマ定義
- LaunchConnection.cursorは、打ち上げデータに付与されているlaunch_date_unixに対応。
- Mission.missionPatchは、打ち上げに対応するアイコン画像情報。大と小があり、デフォルトは大。
- User.tokenは、ここではemailをbase64エンコーディングしたもの。
type Query {
launches(
"""
The number of results to show. Must be >= 1. Default = 20
"""
pageSize: Int
"""
If you add a cursor here, it will only return results _after_ this cursor
"""
after: String
): LaunchConnection!
launch(id: ID!): Launch
me: User
}
type Mutation {
# if false, signup failed -- check errors
bookTrips(launchIds: [ID]!): TripUpdateResponse!
# if false, cancellation failed -- check errors
cancelTrip(launchId: ID!): TripUpdateResponse!
login(email: String): User
}
type TripUpdateResponse {
success: Boolean!
message: String
launches: [Launch]
}
"""
Simple wrapper around our list of launches that contains a cursor to the
last item in the list. Pass this cursor to the launches query to fetch results
after these.
"""
type LaunchConnection {
cursor: String!
hasMore: Boolean!
launches: [Launch]!
}
type Launch {
id: ID!
site: String
mission: Mission
rocket: Rocket
isBooked: Boolean!
}
type Rocket {
id: ID!
name: String
type: String
}
type User {
id: ID!
email: String!
profileImage: String
trips: [Launch]!
token: String
}
type Mission {
name: String
missionPatch(size: PatchSize): String
}
enum PatchSize {
SMALL
LARGE
}
PythonからGraphQL Clientを使用
- 以下のライブラリを使用。
- https://github.com/graphql-python/gql
- pip install --pre gql[all]
import json
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport
transport = AIOHTTPTransport(url="http://127.0.0.1:4000/")
client = Client(transport=transport)
# 打ち上げ予定の一覧を取得 (名前をlaunchesからpageに変更)
query = gql("""
query GetLaunches {
page: launches(pageSize: 3) {
cursor
hasMore
launches {
id
mission {
name
missionPatch(size: SMALL)
}
}
}
}
""")
result = client.execute(query)
print(json.dumps(result, indent=2))
{
"page": {
"cursor": "1605486420",
"hasMore": true,
"launches": [
{
"id": "109",
"mission": {
"name": "Starlink-15 (v1.0)",
"missionPatch": "https://images2.imgbox.com/9a/96/nLppz9HW_o.png"
}
},
{
"id": "108",
"mission": {
"name": "Sentinel-6 Michael Freilich",
"missionPatch": null
}
},
{
"id": "107",
"mission": {
"name": "Crew-1",
"missionPatch": "https://i.imgur.com/BzaSAnx.png"
}
}
]
}
}
# ログインしてトークンを取得
query = gql("""
mutation LoginUser {
login(email: "daisy@apollographql.com") {
token
}
}
""")
result = client.execute(query)
print(json.dumps(result, indent=2))
{
"login": {
"token": "ZGFpc3lAYXBvbGxvZ3JhcGhxbC5jb20="
}
}
# ヘッダーを設定したclientを作成
headers = {'authorization': 'ZGFpc3lAYXBvbGxvZ3JhcGhxbC5jb20='}
transport = AIOHTTPTransport(url="http://127.0.0.1:4000/", headers=headers)
client = Client(transport=transport)
# 打ち上げの予約
query = gql("""
mutation BookTrips {
bookTrips(launchIds: [67, 68, 69]) {
success
message
launches {
id
}
}
}
""")
result = client.execute(query)
print(json.dumps(result, indent=2))
{
"bookTrips": {
"success": true,
"message": "trips booked successfully",
"launches": [
{
"id": "67"
},
{
"id": "68"
},
{
"id": "69"
}
]
}
}
# 打ち上げのキャンセル
query = gql("""
mutation BookTrips {
cancelTrip(launchId: 68) {
success
message
launches {
id
}
}
}
""")
result = client.execute(query)
print(json.dumps(result, indent=2))
{
"cancelTrip": {
"success": true,
"message": "trip cancelled",
"launches": [
{
"id": "68"
}
]
}
}
# ユーザーの情報と予約している打ち上げを取得
query = gql("""
query {
me {
email
trips {
id
mission {
name
missionPatch
}
}
}
}
""")
result = client.execute(query)
print(json.dumps(result, indent=2))
{
"me": {
"email": "daisy@apollographql.com",
"trips": [
{
"id": "67",
"mission": {
"name": "Merah Putih",
"missionPatch": "https://images2.imgbox.com/a8/f5/ZgdsrbqW_o.png"
}
},
"id": "69",
"mission": {
"name": "SAOCOM 1A",
"missionPatch": "https://images2.imgbox.com/66/d2/oVB1ofaZ_o.png"
}
}
]
}
}
その他
- SQLite3では、INTEGER型のカラムに対してPRIMARY KEY制約を設定すると、自動インクリメントとなる。
- Sequelize ORMを使用すると、テーブルのカラムにcreatedAtとupdatedAtが追加される。
- データベースの更新処理は非同期で実行される。