26
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

NextremerAdvent Calendar 2016

Day 18

あとelm

Last updated at Posted at 2016-12-18

Nextremer Advent Calendar 2016の第18日目の記事です。

Node.js Festival 2016でBarakChamoさんのポケモンGraphQLのセッションを聞いて興味を持ちました。

ポケモンGraphQL

http://graph.pokeql.win/
https://github.com/BarakChamo/PokeQL

GraphQLとは

公式より引用

GraphQLはAPI用のクエリ言語であり、既存のデータでこれらのクエリを実行するためのランタイムです。
GraphQLは、APIのデータを完全かつ理解できるように記述し、クライアントに必要なものを何でも尋ねる能力を与え、時間の経過とともにAPIを進化させやすくし、強力な開発ツールを可能にします。

Why GraphQL

  • GraphQLスキーマを用意するだけでドキュメントが用意される。
    • Swggerほどインタラクティブなことはできないが・・・
  • クライアントからQueryをなげて、高度な検索などができるらしい?追ってない。
    (GraphQLの実態はPOST/GETリクエストにqueryや、variableというJSON文字列を渡すことで動作している)

Hello World

ひとまずnode.js+express実装のHello Worldを試してみる。
http://graphql.org/graphql-js/running-an-express-graphql-server/ から

server.js
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');

// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
  type Query {
    hello: String
  }
`);

// The root provides a resolver function for each API endpoint
var root = {
  hello: () => {
    return 'Hello world!';
  },
};

var app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

詳細は公式を見ててらうとして、理解する必要があるのは
schema の hello が呼ばれると root の hello が呼ばれるということです。

また付け加えると、この書き方は GraphQLスキーマ を利用した書き方です。

GraphQLスキーマを使わない場合の書き方は以下のようになります。
Githubとかでよく見るのはこっちかもしれないです。

server.js
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { 
  GraphQLSchema,
  GraphQLString,
} = require('graphql');

var schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      hello: {
        type: GraphQLString,
        resolve: function(source, args) {
          return 'Hello world!';
        }
      }
    }    
  })
});

var app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

どちらも動作は同じです。
スクリーンショット 2016-12-07 12.45.01.png

以前はGraphQLスキーマでは対応していないもの(descriptionとか)がありましたが、最近は対応されているようです。
ちなみにdescriptionの書き方は

# Query description here
type Query {
  # hello endpoint description here
  hello: String
}

です。

ここではGraphQLスキーマを利用して実装していきます。

GraphQLでCRUD

今回実装したソースはこちらです。
試して見たい方はmake startで動かしてみてください。
make logsで起動を確認したら、以下のGraphiQLにアクセスに出来るようになると思います。

自分の開発環境

  • Macbook pro

docker-compose

  • node.js (v6.5.0)/ express + express-graphql + sequelize / babel (ES2015)
  • MySQL
  • elm ... orz(後述)

Userスキーマを作成する

sequelizeでこんなモデルがあるとして、

users.js
sequelize.define('user', {
    id  : {type: dataType.BIGINT(11), primaryKey: true, autoIncrement: true},
    name: {type: dataType.STRING},
    email: {type: dataType.STRING},
  },{
    underscored: true,
    charset: 'utf8',
    timestamps: true,
    paranoid: false,
  })

このモデルをまずGraphQLスキーマで書きます

type User {
  id: Int!
  name: String!
  email: String!
}

User取得

idでUserを取得するQueryを追加します。

type Query {
  user(id: Int) : User
}

IDでUserを取得する処理を追加します。

const rootValue = {
  user: ({id}) => {
    return models.User.findOne({where:{id: id}});
  }
};

これで取得は出来ますが、データがないのでユーザを作成するmutationを作成します。

Mutation

Mutationはデータの作成、更新などの時に利用するものです。

Userを作成するMutationを追加する

input UserData {
  name: String!
  email: String!
}
type Mutation {
  createUser(data: UserData) : User
}

作成時に利用するUserDataを追加しました。(引数ではtypeじゃなくinput)
createUserUserDataを受け取ってUserを作成後にUserを返します。

作成する処理はrootValueに追加します。

const rootValue = {
  user: ({id}) => {
    return models.User.findOne({where:{id}});
  },
  createUser: ({data}) => {
    return models.User.create(data);
  }
};

確認してみる

http://localhost:3000/graphql で確認してみます。
以下のように入力します。

左上のQUERY

mutation ($data: UserData){
  createUser(data: $data) {
    id
    name
    email
  }
}

左下の QUERY VARIABLES

{
  "data": {
    "name": "test",
    "email": "test@example.com"
  }
}

で実行してみると、こんなデータが返ってきたと思います。

{
  "data": {
    "createUser": {
      "id": 1,
      "name": "test",
      "email": "test@example.com"
    }
  }
}

スクリーンショット 2016-12-07 15.28.34.png

そのままですが、Updata/Deleteはこういう感じ。

server.js
  updateUser: ({id, data}) => {
    return models
      .User
      .findOne({where: {id}})
      .then((user) => {
        if (user) {
          return user.update(data).then((updated) => updated);
        } else {
          return null;
        }
      }
    );
  },
  deleteUser: ({id}) => {
    return models.User.destroy({where: {id}});
  }

Updateだけはidを別で指定するようにしました。
スクリーンショット 2016-12-17 13.29.10.png

これでとりあえずGraphQLでCRUDの機構は作れました。

ちなみにGraphQLスキーマを利用しない書き方だとこんな感じになります。

以上です。

追記

本当はフロントエンドをElmでUserを操作する画面を作りたかったんですが、時間が足らず・・・orz

すこしだけElmのことを・・・
ElmはAltJSの一種で、Haskellベースの静的型関数型言語です。
Elm htmlレンダリングエンジンはReactAngularより早いらしいです。
http://elm-lang.org/blog/blazing-fast-html-round-two

実は環境だけは用意してあります。
https://github.com/ichiwa/elm-graphql-docker-demo

make startして、make logsで起動を確認したら、以下にアクセスに出来るようになると思います。

elmwebpack-dev-serverが動いてるので、hot-deploy可能です。
また、/server/publicelm-makeの成果物が入るようになってるので、http://localhost:3000elmが確認できます。
今はelmサンプル書いてありますが、いつか・・・完成させたいところです・・・ :sob:

参考

https://learngraphql.com/
http://qiita.com/Jxck_/items/545e67c556e815620692

26
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?