PLAID Engineer Blog

PLAID Engineer Blog


PLAID Engineer Blog

express-graphql + vue-apollo で GraphQLを試してみた

Hideyuki OkadaHideyuki Okada

こんにちは。エンジニアの@elcih17です。 この記事を書いた頃はインターンでしたが、現在は新卒エンジニアとして日々奮闘しています。

最近、APIの設計に関することを調べていたら「GraphQL」という言葉をよく見かけました。 API用の技術として注目されているそうで、REST APIの次のパラダイムだという意見もあるようです。

どんな技術なのかとても気になりHOW TO GRAPHQLのチュートリアルと簡単な実装をしてみたら、なんとなくGraphQLのことがわかったので書いてみます。

実装例

GraphQL とは

特徴

機能

Query

RESTにおけるGET

idが"1"のユーザーの名前と年齢を取得するGraphQLは次のように書けます。

query {  
  user(id:"1") {
    name
    age
  }
}

response

{
  "data": {
    "user": {
      "name": "elcih17",
      "age": 22
    }
  }
}

Mutation

RESTにおけるPOST、PUT、DELETE

新規ユーザーを作成するGraphQLは次のように書けます。

mutation {  
  createUser(id: "2", name: "KARTE", age:2) {
    name
    age
  }
}

response

{
  "data": {
    "user": {
      "id": "2",
      "name": "KARTE",
      "age": 2
    }
  }
}

Subscription

QueryとMutationの実行をsubscribeできる

試してないので詳しくはわかりませんが、データの更新を受け取って何かしたいときに使えそうです。

GraphQLの何が良いのか

不要なデータの取得と無駄なリクエストを防げる

複数のリソースの欲しいデータを1リクエストで取得できる ので無駄が無くなります。 例えば、あるユーザーの名前と投稿記事タイトルと記事についたコメントを取得するケースを考えてみます。

RESTの場合

  1. GET /users/:id
  2. GET /users/:id/posts
  3. GET /posts/:id/comments
  4. 取得したデータからそれぞれ名前、記事のタイトル、コメントを抽出します

GraphQLの場合

  1. /graphqlに対し下記のようなクエリをリクエストします

必要なデータのみ取得しているのでデータを抽出する必要はありません

query {  
  user(id: "1") {
    name
    posts {
      title
        comments {
        content
      }
    }
  }
}

スキーマと型定義によってAPIが提供する機能が明確になる

GraphiQLが便利

GraphQLサーバーの例(express-graphql)

Query、Mutationの機能紹介で例示したクエリを実行可能にするための実装はこのようになります

GraphQLスキーマの定義 (GraphQLの入出力と型定義)

type Query {  
  user(id: String!): User! // relation
}
type Mutation {  
  createUser(id: String!, name: String!, age: Int!): User // relation
}
type User {  
  id: String!
  name: String!
  age: Int!
}

リゾルバ関数の実装 (GraphQLとバックエンド処理のマッピング)

const users = [  
  {
    id: '1',
    name: 'elcih17',
    age: 22
  }
]
const resolvers = {  
  Query: {
    user: (root, {id}) => {
      return users.find(user => user.id === id)
    }
  },
  Mutation: {
    createUser: (root, args) => {
      const {id, name, age} = args
      const newUser = {id, name, age}
      users.push(newUser)
      return newUser
    }
  }
};

GraphQLオブジェクトとrelation(関連づけ)を使う

標準のオブジェクトタイプ(QueryMutationSubscription)以外にも独自のオブジェクトを定義できます。 また、定義したオブジェクト同士のrelationが可能です。

スキーマ

type User {  
  name: String!
  posts: [Post]
}
type Post {  
   author: User!
   title: String!
}

relationを使えば次のようなクエリを受け取った際にデータを構築するのが簡単になります。

query {  
  user(id: "1") {
    posts {
      title
    }
  }
}

リゾルバ (データの構築)

Query: {  
 user: (root, args) => {
    const {id} = args
    return users.find(user => user.id === id)
 }
},
User: {  
  posts: (author) => {
    const {id} = author
    return posts.filter(post => post.author_id === id)
  }
}

GraphQLクエリの実行の流れについてはこの記事が分かりやすいです。

実装例ではMongoDBに接続して実装してみました。

所感

フロントエンドGraphQL

現在、GraphQLを扱うためのClientに ApolloRelay があります。

RelayはReact用に構築されていてApolloはそのほかのフレームワークにも対応しているようです。
React + Apolloという組み合わせもあるので、Reactを使っている人はRelayとApolloを比べてみても面白いかもしれません。

今回はvue-apolloを使うためにApolloを使用してみました。

一部実装例を載せておきます。

src/main.js

import 'isomorphic-fetch'

import Vue from 'vue'  
import {ApolloClient, createNetworkInterface} from 'apollo-client'  
import VueApollo from 'vue-apollo'  
import App from './App.vue'

const uri = 'http://localhost:3000/graphql'  
const networkInterface = createNetworkInterface({  
  uri,
  transportBatching: true,
})

Vue.use(VueApollo)

const apolloClient = new ApolloClient({networkInterface})  
const apolloProvider = new VueApollo({  
  defaultClient: apolloClient,
})

new Vue({  
  el: '#app',
  apolloProvider,
  render: h => h(App),
})

src/App.vue

<!-- template -->

<div id="app">  
  <h1> Vue Apollo Example </h1>
  <p>{{hello}}</p>

  <label>name</label>
  <input type="text" v-model="userName">

  <label>age</label>
  <input type="number" v-model="userAge">

  <a href="#" @click="createUser">createUser</a>

  <div v-if="createdUser">
    <p>User created!</p>
    <p>name: {{createdUser.name}}</p>
    <p>age: {{createdUser.age}}</p>
  </div>
</div>  
// script

import gql from 'graphql-tag';

const helloGQL = gql`  
  query {
    hello
  }
`;
const createUserGQL = gql`  
  mutation ($userName: String!, $userAge: Int!){
    createUser(name: $userName, age: $userAge) {
      _id
      name
      age
    }
  }
`
export default {  
  name: 'app',
  data() {
    return {
      hello: '',
      userName: '',
      userAge: '',
      createdUser: null
    };
  },
  apollo: {
    hello: {
      query: helloGQL,
    }
  },
  methods: {
    createUser() {
      this.$apollo.mutate({
        mutation: createUserGQL,
        variables: {
          userName: this.userName,
          userAge: this.userAge
        }
      }).then(res => {
        this.createdUser = res.data.createUser
      }).catch(err => {
        console.error(err);
      })
    }
  }
};

詳細は実装例をご覧ください。

所感

プロダクションで使えるか?

現在プロダクション環境でGraphQL APIを運用しているサービスはGithub、Coursera、Twitterなどがあるようです。 GithubのGraphQL API v4を見る限り、運用の仕組みを作れば十分使えそうな気がします。

プロダクションで運用・提供するに当たって考慮する必要がありそうなこと

まとめ

以上で説明を終わります。

GraphQLについて少しはわかっていただけたでしょうか?
今回紹介できなかった機能を使えばもっと色々とできて面白そうなので興味のある方はぜひ試してみてください。

最後に

ウェブ接客プラットフォーム「KARTE」を運営するプレイドでは、 KARTEを支える技術に興味を持つエンジニア(インターンも!)を募集しています。 詳しくはプレイドの採用ページか、Wantedlyをご覧ください。

Comments