HanamiでGraphQL APIを実装してみよう

[Hanami](http://hanamirb.org/)で[GraphQL](http://graphql.org/learn/) APIを実装してみるよ。

### プロジェクトの作成

```
mkdir hanami-blog
cd hanami-blog
```

```rb:Gemfile
# frozen_string_literal: true
source "https://rubygems.org"

gem "hanami", '1.0.0'
```

```
bundle install
```

```
bundle exec hanami new . --application-name=api 
```

```
bundle install
```

### モデルの作成

```
bundle exec hanami generate model user
bundle exec hanami generate model post
```

```rb:20170621155700_create_users.rb
Hanami::Model.migration do
  change do
    create_table :users do
      primary_key :id

      column :name , String, null: false
      column :email, String, null: false

      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false
    end
  end
end
```

```rb:20170621155712_create_posts.rb
Hanami::Model.migration do
  change do
    create_table :posts do
      primary_key :id
      foreign_key :user_id, :users, on_delete: :cascade, null: false

      column :title  , String, null: false
      column :content, String, null: true

      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false
    end
  end
end
```

```
bundle exec hanami db prepare 
```

### Typeの作成

```rb:Gemfile
+ gem 'graphql', '1.6.4'
```

```
bundle install
```

```
mkdir -p apps/api/types
```

```rb:apps/api/application.rb
      load_paths << [
        'controllers',
        'views',
+       'types'
      ]
```

```rb:apps/api/types/user_type.rb
UserType = GraphQL::ObjectType.define do
  name 'User'
  description 'User'
  field :id, types.ID
  field :name, types.String
  field :postss, types[!PostType]
end
```

```rb:apps/api/types/post_type.rb
PostType = GraphQL::ObjectType.define do
  name 'Post'
  description 'Post'
  field :id     , types.ID
  field :title  , types.String
  field :content, types.String
  field :user   , UserType do
    resolve ->(obj, _, _) { UserRepository.new.find(obj.user_id) }
  end
end
```

```rb:apps/api/types/query_type.rb
require_relative 'user_type'
require_relative 'post_type'

QueryType = GraphQL::ObjectType.define do
  name 'Query'
  description 'The query root for this schema'

  field :user do
    type UserType
    argument :id, !types.ID
    resolve ->(_, args, _) { UserRepository.new.find(args[:id]) }
  end

  field :post do
    type PostType
    argument :id, !types.ID
    resolve ->(_, args, _) { PostRepository.new.find(args[:id]) }
  end
end
```

```rb:apps/api/types/hanami_blog_schema.rb
require_relative 'query_type'

HanamiBlogSchema = GraphQL::Schema.define(query: QueryType)
```

```rb:lib/hanami_blog/repositories/post_repository.rb
class PostRepository < Hanami::Repository
  def posts_by_user(user)
    posts.where(user_id: user.id)
  end
end
```

### Controllerの作成

```rb:apps/api/config/routes.rb
root to: "graphql#show"
```

```rb:apps/api/controllers/graphql/show.rb
module Api::Controllers::Graphql
  class Show
    include Api::Action

    def call(params)
      query_variables = params[:vairables] || {}
      self.body = JSON.generate(HanamiBlogSchema.execute(params[:query], variables: query_variables))
    end
  end
end
```

### テスト用データ作成

```
bundle exec hanami c
irb(main):002:0> user = UserRepository.new.create name: "foo", email: "foo@exsample.com"
irb(main):005:0> PostRepository.new.create user_id: user.id, title: "aaa", contest: "AAA"
irb(main):006:0> PostRepository.new.create user_id: user.id, title: "bbb", contest: "BBB"
```


### 試してみる

```
bundle exec hanami s
```

```
curl -XGET -d 'query={ user(id: 1) { name email posts {id title} } }' http://localhost:2300/ | jq .
{
  "data": {
    "user": {
      "name": "foo",
      "email": "foo@exsample.com",
      "posts": [
        {
          "id": "1",
          "title": "aaa"
        },
        {
          "id": "2",
          "title": "bbb"
        }
      ]
    }
  }
}
```

```
curl -XGET -d 'query={ post(id: 1) { title user { name } } }' http://localhost:2300/ | jq . 
{
  "data": {
    "post": {
      "title": "aaa",
      "user": {
        "name": "foo"
      }
    }
  }
}
```

以上。
[Hanami](http://hanamirb.org/) x [GraphQL](http://graphql.org/learn/)のニッチ x ニッチでどこに需要があるのかって感じですが、なにかのご参考になれば。

### 参考

https://blog.simplificator.com/2016/12/07/getting-started-with-hanami-and-graphql/