まとめ
- GraphQLについて言及されている場面に遭遇したので、調べてみたところ以下のページを見つけたので、チュートリアルに沿って動かしてみた。
- 言語はpythonを選択。今のところ読み解けるのがpythonだけだというのと、知りたいのはサーバ側の挙動でクライアント側ではなかったから、というのが理由。
次やってみること
- 同じ作業をEC2でやってみよう。
チュートリアルからのメモ
1. ローカル環境の設定
pythonは3.6を使うこと、と明示されていたので、初めてvirtualenvを使用してみる。pyenv local
だけでうまくいくのかも知れなかったけど、以前にEdukitで指定のパッケージがインストールできず、あれやこれやあくせくした経験があったので、チュートリアル用の環境をvirtualenvで分けることにした。
> pyenv install 3.6.15 # 指定されているpython3.6をインストール
> pyenv virtualenv 3.6.15 graphql3.6.15 # 環境作成
> cd workdir/python
> mkdir graphql
> cd graphql/
> pyenv local graphql3.6.15 # フォルダに環境を紐付け
2. DjangoとGrapheneのインストール
- Djangoは、Pythonで実装されたWebアプリケーションフレームワーク。
- Graphene is a library that provides tools to implement a GraphQL API in Python using a code-first approach.
サーバの立ち上げはめちゃくちゃ簡単。お決まりのHello World!的な。素人としては、この拍子抜けする感じにはまだ慣れない。ともあれ、以下のコマンドを叩けば、ローカル環境にサーバが立ち上がる。
> pip install django==2.1.4 graphene-django==2.2.0 django-filter==2.0.0 django-graphql-jwt==0.1.5
> django-admin startproject hackernews
> cd hackernews
> python manage.py migrate
> python manage.py runserver
ブラウザでhttp://localhost:8000
にアクセスすると、サーバが立ち上がっている。django-admin startproject hackernews
で立ち上がったサーバの構成は以下の通り。全く関係ないけど、treeパッケージをインストールしたのが最近で、楽しくてやたらとtree
と打っている。
.
└── hackernews
├── db.sqlite3
├── hackernews
│ ├── __init__.py
│ ├── __pycache__ # 下にファイルあり
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
3. Graphene Djangoのコンフィグ
正直、このセクションは呪文。。。とりあえずやったことを列挙しておく。
- settings.pyの中の
INSTALLED_APPS
変数に'graphene-dgango'
を追加 - settings.pyの中で新たな変数
GRAPHENE
を設定
GRAPHENE = {
'SCHEMA': 'hackernews.schema.schema',
}
4. Linksアプリ立ち上げ
次にLinksというアプリケーションを立ち上げる。コマンドはpython manage.py startapp links
。Djangoはプロジェクトをアプリケーションごとに分けると説明されていたが、その意味はよく分からない。いずれ分かるんだろう。。。その他にいくつかコンフィグ設定。
- links/models.pyにLinkのモデルを追加する。
- hackernews/settings.pyの
INSTALLED_APPS
に'links'
を追加 - 新たなDBテーブルを作成する。コマンドは以下の2行
python manage.py makemigrations
- `python manage.py migrate'
- テーブルの中身をShellから入力する
.
├── db.sqlite3
├── hackernews # 変化なしなので下のフォルダ省略
├── links # python manage.py startapp linksで追加
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── manage.py
5. TypeとSchemaの作成
- Type: いくつかのFieldを含むオブジェクト (例: type Post { title: String! text: String! datePublished: DateTime! ...})
- Schema: Typeの集合
- Resolver: Fieldの値を返すメソッド
ここでは、hackernews/schema.pyとlinks/schema.pyを新たに作成してスキーマを作成する。と書いているけれど、何が設定されているのかは分からずにとりあえず進んでいる。
.
├── db.sqlite3
├── hackernews
│ ├── __init__.py
│ ├── __pycache__
│ ├── schema.py # 新たに作成
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── links
│ ├── __init__.py
│ ├── __pycache__
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ ├── models.py
│ ├── schema.py # 新たに作成
│ ├── tests.py
│ └── views.py
└── manage.py
6. GraphiQLの導入
GraphiQL is a graphical interactive in-browser GraphQL IDE
hackernews/urls.pyにGraphiQLを読み込むラインを追加することで、ブラウザからのクエリが可能になる。
from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
urlpatterns = [
path('admin/', admin.site.urls),
path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
]
7. Mutation
The process of sending data to server is called mutation. Defining it is pretty similar on how you’ve defined the query.
ここでは、links/schema.pyで定義して、hackernews/schema.pyで呼び出す状態にする。これらのコードをアップデートした後からは、http://localhost:8000/graphql/
からMutationの操作が可能になる。以下は追加例
mutation {
createLink (
url: "http://github.com",
description: "Lots of code!"
) {
id
url
description
}
}
8. Userの作成
ここではpython manage.py startapp <APP_NAME>
ではなくて、userという名前のフォルダを新たに作って、そこにschema.pyを作る、というステップを踏んでいる。ただその後はlinksの時と同様、user/schema.pyにMutationを実行するクラスを作成して、hackernews/schema.py (root schema) の方で、そのクラスを呼び出す命令を追加すると、Localhost上からcreateUser
が実行できるようになる。同じ操作をQueryについても実行することで、userにクエリを走らせることも可能になる。
mutation {
createUser(username: "shiro", email: "shiro@example.com", password: "12345678") {
user {
id
username
email
}
}
}
9. User Authentication
このチュートリアルでは、JWT (JSON Web Tokens)を実装する方法を紹介している。Authenticationについてはとりあえず深追いしないで先に行く。JWTに関する情報は色々あるみたい。久々登場のhackernews/setting.pyにMIDDLEWAREの変数を追加する。
MIDDLEWARE = [
# all the middlewares
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
さらに、変数GRAPHENEにMIDDLEWARESを追記し、新たな環境変数AUTHENTICATION_BACKENDSも追加する。
GRAPHENE = {
'SCHEMA': 'hackernews.schema.schema',
'MIDDLEWARES': [
'graphql_jwt.middleware.JSONWebTokenMiddleware',
]
}
AUTHENTICATION_BACKENDS = [
'graphql_jwt.backends.JSONWebTokenBackend',
'django.contrib.auth.backends.ModelBackend',
]
コードをアップデートしてさて、Localhostにアクセスしようと思ったら、エラーが出て立ち上がらない。エラーメッセージから調べたら、変数名に注意!というやりとりを発見。ありがたいありがたい。
もう一個引っかかりポイントが。無事にhttp://localhost:8000/graphql/
にアクセスできるようになった、と思ったら今度はToken発行がうまくいかない。で、これも調べるとStackOverflow様様で解決策発見。pip install PyJWT==1.7.0
で無事に動きましたとさ。こういう解決策を探してくるところまでチュートリアルの一部だと考えると、私みたいな初心者には逆になかなか良いな、このチュートリアル。(まあ、多分直してないだけだと思うけど)
で、ここで発行されたトークンをDebuggerに入れたら、ちゃんとちゃんとちゃんと元の情報が出てくるのね。なんか感動してしまった。
この後、Insomniaを使って、あれやらこれやら確認する、なんてステップに入っていったので割愛。適当にわからないことは飛ばしながら進みます。
10. Search
Searchは、links/schema.py内にあるQueryのClassにSearch機能を追記することで追加する。
# ..code
# After the imports, add
from django.db.models import Q
# ...code
class Query(graphene.ObjectType):
# Add the search parameter inside our links field
links = graphene.List(LinkType, search=graphene.String())
# Change the resolver
def resolve_links(self, info, search=None, **kwargs):
# The value sent with the search parameter will be in the args variable
if search:
filter = (
Q(url__icontains=search) |
Q(description__icontains=search)
)
return Link.objects.filter(filter)
return Link.objects.all()
最後に
途中のステップをいくつか飛ばしたけれど、DBについて何も知らなかったところから始めたので、基本的な考え方も透けて見えてきてかなり勉強になった。誰のためにもならない記事になってしまったけれど、これを描きながらチュートリアルを進めることで、曖昧な理解にとどめなかった箇所がいくつかあるので個人的にはとても満足。