あとelm


[Nextremer Advent Calendar 2016](http://qiita.com/advent-calendar/2016/nextremer)の第18日目の記事です。

Node.js Festival 2016で[BarakChamo](https://github.com/BarakChamo)さんのポケモンGraphQLのセッションを聞いて興味を持ちました。

### ポケモンGraphQL
http://graph.pokeql.win/
https://github.com/BarakChamo/PokeQL


## GraphQLとは
[公式](http://graphql.org/)より引用 
> 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/ から

```javascript: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とかでよく見るのはこっちかもしれないです。

```javascript: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](https://qiita-image-store.s3.amazonaws.com/0/87900/060ffcd7-ceae-1b87-64f5-05c3364f1b56.png "スクリーンショット 2016-12-07 12.45.01.png")

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

```
# Query description here
type Query {
  # hello endpoint description here
  hello: String
}
```
です。

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

## GraphQLでCRUD
今回実装したソースは[こちら](https://github.com/ichiwa/elm-graphql-docker-demo)です。
試して見たい方は`make start`で動かしてみてください。
`make logs`で起動を確認したら、以下の[GraphiQL](https://github.com/graphql/graphiql)にアクセスに出来るようになると思います。

http://localhost:3000/graphql

### 自分の開発環境
- Macbook pro

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

## Userスキーマを作成する
sequelizeでこんなモデルがあるとして、

```js: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`)
`createUser`は`UserData`を受け取ってUserを作成後に`User`を返します。

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

```js
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](https://qiita-image-store.s3.amazonaws.com/0/87900/54dda7ff-e571-0307-e0d0-664f8532229a.png "スクリーンショット 2016-12-07 15.28.34.png")

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

```javascript: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](https://qiita-image-store.s3.amazonaws.com/0/87900/ca94615e-7b05-b316-8436-1fdbd86e376d.png "スクリーンショット 2016-12-17 13.29.10.png")

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

ちなみにGraphQLスキーマを利用しない書き方だと[こんな感じ](https://github.com/ichiwa/elm-graphql-docker-demo/blob/master/server/backup.js)になります。

以上です。

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

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


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

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

http://locahost:8080

`elm`は`webpack-dev-server`が動いてるので、`hot-deploy`可能です。
また、`/server/public`に`elm-make`の成果物が入るようになってるので、`http://localhost:3000`で`elm`が確認できます。
今は`elm`の[サンプル](https://guide.elm-lang.org/architecture/user_input/buttons.html)が[書いてあります](https://github.com/ichiwa/elm-graphql-docker-demo/blob/master/client/Main.elm)が、いつか・・・完成させたいところです・・・ :sob: 

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