概要 †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 から動作確認可能。 |