概要 †GraphQLはFacebookにより開発されたオープンソースの言語。
以降では、以下のライブラリを使用してサーバ 及び クライアント処理の作成を行う。
目次 †
GraphQLの基礎 †クエリとミューテーション †TODO:
Fields †Arguments †Aliases †Fragments †Operation Name †Variables †Directives †Mutations †Inline Fragments †スキーマとタイプ †サーバ側の環境構築 †pythonライブラリ graphene を使用してサーバ環境を構築する。 準備 †pythonの仮想環境を作って graphene をインストールしておく。 pip install pipenv pipenv --python 3.7 pipenv install graphene pipenv install flask pipenv install flask-graphql pipenv install flask-cors 動作確認用に仮想環境のシェルを起動しておく pipenv shell スキーマ 及び リゾルバ関数の定義 †schema.py # サンプルデータ sample_users = { "1": {"id":1, "name":"Ichiro", "age":10}, "2": {"id":2, "name":"Jiro", "age":20}, "3": {"id":3, "name":"Saburo", "age":30} } class User(graphene.ObjectType): id = graphene.ID() name = graphene.String() age = graphene.Int() class Query(graphene.ObjectType): user = graphene.Field(User, id=graphene.ID()) users = graphene.Field(graphene.List(User)) def resolve_user(self, info, id): # resolve_変数名 """ID指定のユーザ取得.""" user_info = None if id: # if info.variable_values["id"] でも可 user_info = sample_users.get(id) # sample_users.get(info.variable_values["id"]) if user_info: return User(**user_info) else: return None def resolve_users(self, info, **kwargs): """ユーザ一覧の取得.""" return [ User(**sample_users[id]) for id in sample_users] class CreateUser(graphene.Mutation): class Arguments: name = graphene.String() age = graphene.Int() created = graphene.Boolean() user = graphene.Field(lambda: User) def mutate(root, info, name, age): id = len(sample_users) + 1 sample_users[str(id)] = {"id":id, "name":name, "age":age} user = User(id=id, name=name, age=age) return CreateUser(user=user, created=True) class MyMutations(graphene.ObjectType): create_user = CreateUser.Field() schema = graphene.Schema(query=Query, mutation=MyMutations) スキーマ単体での動作確認 †test_schema.py from schema import schema if __name__ == "__main__": # ユーザを作成 query = """ mutation createMutation { createUser(name:"Shiro", age:40) { user { id name age } created } } """ result = schema.execute(query) print(result.data) # 一覧を取得 query = """ query ListQuery { users { id, name, age } } """ result = schema.execute(query) print(result.data) # IDを指定して取得 query = """ query SampleQuery($id: ID) { user(id: $id) { id, name, age } } """ result = schema.execute(query, variable_values={"id": 3}) print(result.data) 動作確認の実行 python test_schema.py OrderedDict([('createUser', OrderedDict([('user', OrderedDict([('id', '4'), ('name', 'Shiro'), ('age', 40)])), ('created', True)]))]) OrderedDict([('users', [OrderedDict([('id', '1'), ('name', 'Ichiro'), ('age', 10)]), OrderedDict([('id', '3'), ('name', 'Saburo'), ('age', 30)]), OrderedDict([('id', '2'), ('name', 'Jiro'), ('age', 20)]), OrderedDict([('id', '4'), ('name', 'Shiro'), ('age', 40)])])]) OrderedDict([('user', OrderedDict([('id', '3'), ('name', 'Saburo'), ('age', 30)]))]) Webサーバの実装 †app.py from schema import schema from flask import Flask from flask_graphql import GraphQLView from flask_cors import CORS app = Flask(__name__) app.debug = True CORS(app) # ローカルでの動作確認用にクロスオリジンを許可 app.add_url_rule( '/', view_func=GraphQLView.as_view( 'graphql', schema=schema, graphiql=True # テスト時などにブラウザから利用できるAPIコンソールをONにしておく ) ) if __name__ == '__main__': app.run() サーバ起動 python app.py WebAPIとしての動作確認 †# 一意検索 curl http://127.0.0.1:5000/ --data query='query { user(id: "1") { id, name, age}}' {"data":{"user":{"id":"1","name":"Taro","age":20}}} # 一覧検索 curl http://127.0.0.1:5000/ --data query='query { users{ id, name}}' {"data":{"users":[{"id":"3","name":"Saburo"},{"id":"1","name":"Ichiro"},{"id":"2","name":"Jiro"}]}} # 追加 curl http://127.0.0.1:5000/ --data query='mutation { createUser(name:"Shiro", age:40) { user {id, name, age}, created }}' {"data":{"createUser":{"user":{"id":"4","name":"Shiro","age":40},"created":true}}} 尚、graphiql=True で起動している場合は、ブラウザから任意のQueryを発行する為のコンソール画面も提供されるので、そちらから確認する事も可能。 クライント処理の作成 †実際にAPIを利用するクライアント側の処理を Vue.js 及び Vue Apollo で作成する。 プロジェクトの作成 †vue create sample-project ? Please pick a preset: Manually select features ? Check the features needed for your project: TS, Router, Linter ? Use class-style component syntax? Yes ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? No ? Use history mode for router? (Requires proper server setup for index fallback in production) No ? Pick a linter / formatter config: Basic ? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? No GraphQLクライアントのインストール †GraphQLクライアントとして Vue Apollo を使用する。 cd sample-project vue add apollo クライアント処理の作成 †src/main.ts import Vue from 'vue' import App from './App.vue' import router from './router' import { createProvider } from './vue-apollo' Vue.config.productionTip = false new Vue({ router, apolloProvider: createProvider(), render: h => h(App) }).$mount('#app') ※ vue add apollo した時点で自動的に更新されている為、特に自分で変更はしていない。 src/App.vue <template> <div id="app"> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/graphql">GraphQL Sample</router-link> <!-- 追加 --> </div> <router-view/> </div> </template> : : src/router/index.ts : : const routes = [ : : // 以下を追加 { path: '/graphql', name: 'graphql', component: () => import('../views/GraphQL.vue') } ] src/views/GraphQL.vue <template> <div class="graphql"> GraphQL Example! <div> <table id="users_table"> <thead> <tr> <th>id</th><th>name</th><th>age</th> </tr> </thead> <tbody> <tr v-for="u in users" v-bind:key="u.id"> <td>{{ u.id }}</td><td>{{ u.name }}</td><td>{{ u.age }}</td> </tr> </tbody> </table> <hr> <button v-on:click="addUser()">追加</button> <div> 名前: <input type="text" v-model="newName"><br /> 年齢: <input type="text" v-model="newAge"><br /> </div> </div> </div> </template> <script> import gql from 'graphql-tag' export default { name: 'graphql', data: function () { return { users: [], newName: "Sample", newAge: 10 } }, apollo: { users: gql`query getUsers { users {id, name, age} }`, }, methods: { addUser() { this.$apollo.mutate({ mutation: gql`mutation createMutation($name: String, $age: Int) { createUser(name: $name, age: $age) { user { id, name, age } created } } `, variables: { name: this.newName, age: this.newAge }, refetchQueries: ["getUsers"] }).then((data) => { console.log(data) }).catch((error) => { console.error(error) }) } } } </script> <style> #users_table { margin: 0 auto; border-collapse: collapse; border-spacing: 0; } #users_table th, #users_table td { border: 1px solid #333; padding: 2px 4px; } #users_table th { background: #ddd; } #users_table td { background: #fff; } </style> 設定ファイルの作成 †.env.development NODE_ENV="development" VUE_APP_GRAPHQL_HTTP="http://localhost:5000/graphql" VUE_APP_GRAPHQL_WS= .env.production NODE_ENV="production" VUE_APP_GRAPHQL_HTTP="/graphql" VUE_APP_GRAPHQL_WS= vue.config.js module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/static/' : '/' } 動作確認用のVueのサーバを起動 †cd sample-project npm run serve あとは http://localhost:8080/ にアクセスして画面からAPI が利用できる事を確認していく。 ビルド †クライアント処理のビルド †tsconfig.json { "compilerOptions": { : "allowJs": true, : ※ apolloProvider というプロパティがダメだと怒られるので、allowJs: true にしておく。 ビルド cd sample-project npm run build サーバ側(flask)のstatic フォルダにコピー rm -rf path_to/static cp -R dist path_to/static ※path_to は flask側のフォルダ 以上で flask のみ起動した状態で http://localhost:5000/static/index.html から動作確認可能。 |