Rails + React + GraphQL + ApolloClientで画面表示されるまでのハンズオン！

## 最初に
こんにちは！初めまして！！
１１日目のカレンダーを担当させていただく佐々木です。:sunny:

さて、今回は私自身バックエンドを学んだら、その後どうしよう？と考えたところ
フロントエンドもできればかっこよくね？？みたいな安直な考えで最初勉強し始めたのですが、
つまずく点が個人的にとても多く、とても苦労したので少しでも参考になればと思います。

ぜひ、Railsを活用して見たいという人に見ていただきたいです。

### 使うものについてさらっと説明
#### Ruby on Rails
言わずと知れたスピーディーでweb開発ができる優れもの:star:
https://guides.rubyonrails.org/
### React
モダンなフロントエンドのフレームワークの一つです。
hooksめちゃ楽しいです！！:hourglass:
https://ja.reactjs.org/
####GraphQL
RESTapiの代わりになるものという認識です。（間違っていましたら申し訳ありません。）
なんかすごい直感的にかけるイメージ強いです。
https://graphql.org/
#### ApolloClient
ReactとRailsをつなぐものです。 
https://www.apollographql.com/docs/react/api/core/ApolloClient/
## 完成のファイル
https://github.com/soutaschool/demo-sample
## 実装
### バックエンド編
#### railsアプリの雛形の作成
早速ターミナルに打ち込んで雛形を作成しましょう。 
※今回は**APIモードを使用**します 

```:ターミナル
$ rails new demo --api
```

#### モデルの作成
早速`cd demo`を入力して**demo**ディレクトリに移動しましょー！ 
移動できたところで

```:ターミナル
$ rails g model User name:string age:integer
$ rails g model Book title:string body:text user:references
$ rails db:migrate RAILS_ENV=development
```
として**User**と**Book**モデルを作成&反映させていきましょう。 
**※** referencesについては[こちら](https://qiita.com/ryouzi/items/2682e7e8a86fd2b1ae47)が参考になります。

#### リレーションの定義
モデルの作成ができましたら、Bookモデルの方は**references**によって自動でリレーションできているので、Userモデルの方のみ設定していきましょう。

```ruby:user.rb
class User < ApplicationRecord
  # 以下を記述
  has_many :books
end
```

#### 各種gemのインストール
それぞれgemをインストールしていきます。 
**graphql** → GraphQLの開発に必須
**graphiql-rails** → railsのdevelopment環境でGraphQLの動作確認をできるようにする
**faker** → ランダムなダミーデータの自動作成

```ruby:Gemfile
group :development do
  gem 'listen', '~> 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  # 追加
  gem 'graphiql-rails'
  gem 'faker'
end

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
# 追加
gem 'graphql'
```
と記述できたら

```:ターミナル
$ bundle install
```
しておきましょう！

#### GraphQLのインストール
佐伯ほど追加したgraphqlを利用するために

```:ターミナル
$ rails g graphql:install
```
とすることで`app`ファイルの直下にgraphql関連のものが追加され

```ruby:routes.rb
Rails.application.routes.draw do
  # 以下の部分が自動で追加されている
  post "/graphql", to: "graphql#execute"
end
```
となります。
また、、以下のコマンドをコマンドをターミナル上で扱うことができるようになります。

```
Graphql:
  graphql:enum
  graphql:install
  graphql:interface
  graphql:loader
  graphql:mutation
  graphql:object
  graphql:scalar
  graphql:union
```

#### ダミーデータの作成
先ほどインストールした**faker**を利用して

```ruby:seeds.rb
10.times do
  user = User.create!(name: Faker::Name.name, age: rand(1..100))
  10.times do
    user.books.create!(title: Faker::Lorem::sentence(word_count: 5),
                       body: Faker::Lorem::paragraph(sentence_count: 4))
  end
end
```
として

```terminal:ターミナル
$ rails db:seed
$ rails c # コンソール画面へ
irb(main):001:0> User.all
   10個のUser情報の表示
irb(main):002:0> Book.all
   10個のBook情報の表示
irb(main):003:0> exit
```
と確認することができれば正しくデータが入っているかわかります！

#### GraphQLのオブジェクトの作成
最初にターミナルにてgraphqlのオブジェクトを作成していきます。

```:ターミナル
$ rails g graphql:object user
$ rails g graphql:object book
```
と記述することで`app/graphql/types`直下に**オブジェクト名_type.rb**が
作成されると思います。

#### タイプファイルの確認と追加
ここでの型定義は`migrate`ファイルに少し近い記述方法となります。
確認と追加を行いましょう」。

```ruby:user_type.rb
module Types
  class UserType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: true
    field :age, Integer, null: true
    field :created_at, GraphQL::Types::ISO8601DateTime, null: false
    field :updated_at, GraphQL::Types::ISO8601DateTime, null: false

    # ここから記述
    field :books, [Types::BookType], null: false
    field :books_object_count, Integer, null: false

    # ユーザーに紐づけられたBookオブジェクトの数を数える
    def books_object_count
      object.books.count
    end
  end
end
```
```ruby:book_type.rb
module Types
  class BookType < Types::BaseObject
    field :id, ID, null: false
    field :title, String, null: true
    field :body, String, null: true
    field :user_id, Integer, null: false
    field :created_at, GraphQL::Types::ISO8601DateTime, null: false
    field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
  end
end
```
#### Queryの定義
簡単にQueryの説明をしておくと**データを取得するもの**という認識で大丈夫だと思います。
※補足としてここの部分の詳細は一つのデータのみを取得するので配列で括らなくで大丈夫です。

```ruby:query_type.rb
module Types
  class QueryType < Types::BaseObject
    # 全てのデータの取得
    field :users, [Types::UserType], null: false
    def users
      User.all
    end

    # 引数の番号のデータを取得
    field :user, Types::UserType, null: false do
      argument :id, ID, required: true
    end
    def user(id:)
      User.find(id)
    end
  end
end
```
#### railsでGraphqlを確認できるようにする
ここまで、できたら実際に確認してみましょう。
実際に``とアクセスしてGraphQLの画面を開けるように

```ruby:routes.rb
Rails.application.routes.draw do
  # ここの部分を記述
  if Rails.env.development?
    mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: "graphql#execute"
  end

  # 以下の部分が自動で追加されている
  post "/graphql", to: "graphql#execute"
end
```
と記述し

```ruby:application.rb
require_relative 'boot'

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_mailbox/engine"
require "action_text/engine"
require "action_view/railtie"
require "action_cable/engine"
require "sprockets/railtie" # ここの部分のコメントアウトを取る
require "rails/test_unit/railtie"
```

さらに
`app`フォルダの直下に`assets/config/manifest.js`を作成し

```javascript:manifest.js
//= link graphiql/rails/application.css
//= link graphiql/rails/application.js
```
とすることでdevelopment環境でのみ**アクセスが可能**となります。

![スクリーンショット 2020-12-03 8.39.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543281/9d2b3483-f651-756c-f530-9f6664c87aad.png)
このような画面になればOKです！！

#### データを確認する
ここまでできたら実際にデータを確認してみましょう！
##### 全てのデータ
ここでのデータは
Userは**10人**（IDは全部で10）
1人のUserに紐づけられている本は10冊なので合計で**100冊**（IDは全部で100）
となります。
![スクリーンショット 2020-12-03 11.28.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543281/cef13b73-e236-1bf9-3332-fe081dc7fa25.png)

##### 指定されたデータ
![スクリーンショット 2020-12-03 11.30.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543281/4579744e-d655-7065-a19f-fc76dc7afaac.png)
となります。


### フロントエンド編
#### フロント部分の雛形を作成
では、reactの雛形を作成していきましょう。
今回は`create-react-app`を使用してお手軽reactアプリケーションを作成します。

```terminal:ターミナル
$ yarn global add create-react-app #create-react-appコマンドをグローバルで使用できるようにする
$ create-react-app front-demo #雛形を作成
```

#### CORSの設定
**CORSとは**`CRUDリクエストをサーバーがフィルター`するものという認識です！
参考サイトとして[こちら](https://qiita.com/mtoyopet/items/326ba62d485e9ef0dacd)をご参照ください
デフォルトでコメント状態となっているのでコメントアウトしてあげましょう！

```ruby:Gemfile
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors' #コメントアウト
```
として`bundle install`しましょう！
続いて

```ruby:cors.rb
# Be sure to restart your server when you modify this file.

# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.

# Read more: https://github.com/cyu/rack-cors

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```
としましょう。

##### 画面に表示させる
雛形で作成したものから
![スクリーンショット 2020-12-10 17.28.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543281/ba7a6670-c9ed-f08d-0909-367dcd9074db.png)

**以上のようなものをそれぞれインストールのほどお願いします。**
そしてデフォルトで作成されている`App.js`ファイルを

```react:App.js
import React from 'react';
import ApolloClient from 'apollo-boost'
import { ApolloProvider } from "@apollo/react-hooks"
import Demo from "./Demo";
const client = new ApolloClient({
  uri: 'http://localhost:3000/graphql'
})

const App = () => {
    return (
        <ApolloProvider client={client} >
            <div className="App">
                <Demo/>
            </div>
        </ApolloProvider>
    );
}

export default App;
```
としていき、srcファイル内に`Demo.js`というファイルを作成していただき

```react:Demo.js
import React from 'react';
import {useQuery} from '@apollo/react-hooks';
import gql from 'graphql-tag';

const GET_USERS = gql`
    {
        users{
            id
            name
            age
            books{
                id
                title
                body
            }
        }
    }
`;

const Demo = () => {
    const {loading, error, data} = useQuery(GET_USERS);
    const style = {
        color: "red"
    };

    if (loading) return 'ロード中....';
    if (error) return `Error ${error.message}`;
    return (
        <React.Fragment>
            {data.users.map(user => (
                <div key={user.id}>
                    <h1>{user.name}</h1>
                    <h2>{user.age}</h2>
                    {user.books.map(book => (
                        <div key={book.id}>
                            <h1>{book.title}</h1>
                            <h2>{book.body}</h2>
                        </div>
                    ))};
                    <h1 style={style}>ここまでが{user.id}回目！</h1>
                </div>
            ))}

        </React.Fragment>
    )
};

export default Demo;
```
としましょう！

#### 画面で確認する
![スクリーンショット 2020-12-10 17.26.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543281/d0f45385-3323-4d29-3fdf-2d3059c0bbc3.png)
と表示されれば完璧です！
文字の部分はFakerの文字になるのでランダムです。

以上が実装の一例となります！

ここからさらにGET_USERやユーザーの作成などの拡張ができると思うのでお手隙の際にぜひ試してみてください:runner_tone1:

記事を見てくださり**ありがとございました！！！**:new_moon: