5
0

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 3 years have passed since last update.

HameeAdvent Calendar 2020

Day 25

RailsでGraphQLをやってみる

Last updated at Posted at 2020-12-24

はじめに

メリークリスマス!
クリスマスはイエス・キリストの誕生日だと思っていたんですが、あくまで誕生を祝う日であって、イエス・キリストの誕生日ではないということを最近知りました。

クリスマス(英: Christmas)は「キリストのミサ」という意味で、一部の教派が行うイエス・キリストの降誕祭[1]。あくまで誕生を祝う日であって、イエス・キリストの誕生日ではない[2]。
毎年12月25日に祝われるが、正教会のうちユリウス暦を使用するものは、グレゴリオ暦の1月7日に該当する日にクリスマスを祝う[3][4]。ただし、キリスト教で>最も重要な祭と位置づけられるのはクリスマスではなく、復活祭である[5][6][7][8]。
キリスト教に先立つユダヤ教の暦、ローマ帝国の暦、およびこれらを引き継いだ教会暦では, 現代の常用時とは異なり、日没を一日の境目としているので、クリスマス・イヴと呼ばれる12月24日夕刻から12月25日朝までも、教会暦上はクリスマスと同じ日に数えられる[9]。したがって、教会暦ではクリスマスは「12月24日の日没から12月25日の日没まで」である。
https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AA%E3%82%B9%E3%83%9E%E3%82%B9

せっかくクリスマスの日に担当になったので、クリスマスに絡めたネタを書こうと思っていたのですが、全く思いつかないので普通に記事を書きます。

これはHameeアドベントカレンダー25日目の記事です。

背景

普段、ふるさと納税関連のお仕事をしています。現在、アプリケーションエンジニアチームは2人。複数のアプリケーションを開発していて、 API開発での課題感がでてきました。

  • エンドポイントがたくさん。IFを実装するたびに都度作成していて、管理しにくくなってきている
  • スピード重視・少人数のため、APIドキュメント管理できていない

上記のこと、またいまさらですがGraphQLをやってみたかったので、やってみることにしました。

GraphQLとは

GraphQLの紹介は多くの方が記事にされているので、簡単にまとめます。(RESTとの違いについてもここでは説明しません)
GraphQLはFacebookが開発しているWeb APIのための規格で、「クエリ言語」と「スキーマ言語」からなります。
以下の特徴があります。

  • スキーマ(Web APIの仕様)を構築し、クエリでどのようなデータを取得するかを表現
  • 単一のエンドポイント(/graphql)のみ。ほしいデータはhttp bodyに明記して取得可能
  • GraphiQLという専用のIDEを使えば、スキーマ等のドキュメントも確認可能
  • クエリ言語にはデータ取得系のquery、データ更新系のmutation、pub/subモデルでサーバーサイドのイベントを受け取るsubscriptionがある

以下の記事は大変参考にさせて頂きました。
「GraphQL」徹底入門 ─ RESTとの比較、API・フロント双方の実装から学ぶ
Web API初心者と学ぶGraphQL

Hello worldやってみる

今回はRubyライブラリとして提供されているgraphql、graphql-railsを使って、GraphQLをやってみます。

実行環境

version
ruby 2.6.3
rails 5.2.3
gem graphql 1.11.6
gem graphql-rails 1.7.0

手順

Gemfile
gem 'graphql'

group :development, :test do
  gem 'graphiql-rails'
end

Gemfileに記載後、以下のコマンドを実行します。
実行後に以下のファイル郡が作成されます。

$ bundle install
$ rails generate graphql:install
$ bundle exec rails g graphql:install
create  app/graphql/types
      create  app/graphql/types/.keep
      create  app/graphql/app_schema.rb
      create  app/graphql/types/base_object.rb
      create  app/graphql/types/base_argument.rb
      create  app/graphql/types/base_field.rb
      create  app/graphql/types/base_enum.rb
      create  app/graphql/types/base_input_object.rb
      create  app/graphql/types/base_interface.rb
      create  app/graphql/types/base_scalar.rb
      create  app/graphql/types/base_union.rb
      create  app/graphql/types/query_type.rb
add_root_type  query
      create  app/graphql/mutations
      create  app/graphql/mutations/.keep
      create  app/graphql/mutations/base_mutation.rb
      create  app/graphql/types/mutation_type.rb
add_root_type  mutation
      create  app/controllers/graphql_controller.rb
       route  post "/graphql", to: "graphql#execute"
     gemfile  graphiql-rails
       route  graphiql-rails
Gemfile has been modified, make sure you `bundle install`

また、専用のIDEも/graphiqlで確認できます。
(私の場合は初期画面ではQUERY VALIABLESのみしか見えない状態でしたが、リロードで表示できました)
最初に以下のクエリを実行(左側)して、実行結果が正しく表示されていれば(右側)、Hello world成功です。

query {
  testField
}

また、右上のDocs>queryを選択すると、API側で設定されているスキーマ定義やクエリを確認することができます。
image.png

サンプルデータでやってみる

年末なので、ふるさと納税に絡めて、リレーションがある3つのサンプルデータを作ってみます。

  • local_governments: 自治体
    • id: 自治体ID
  • suppliers: 返礼品事業者
    • id: 返礼品事業者ID
    • local_government_id: 自治体ID
  • return_gifts: 返礼品
    • id: 返礼品ID
    • supplier_id: 返礼品事業者ID
    • name: 返礼品名

上記のデータは以下のリレーションがあります。
IMG_1926.JPG

スキーマ定義

まずはスキーマ定義からです。
各テーブルと対になる形で定義していきます。

app/graphql/types/local_government_type.rb
module Types
  class LocalGovernmentType < Types::BaseObject
    field :id, ID, null: false
    field :suppliers, [Types::SupplierType], null: false

    field :created_at, GraphQL::Types::ISO8601DateTime, null: false
    field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
  end
end
app/graphql/types/supplier_type.rb
module Types
  class SupplierType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    def name
      object.info['supplier_name']
    end
    field :return_gifts, [Types::ReturnGiftType], null: false

    field :created_at, GraphQL::Types::ISO8601DateTime, null: false
    field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
  end
end

↑objectという変数でResolverからオブジェクトが渡されます。suppliers.nameは本来存在しないのですが、json型で定義されているデータを渡してます。decoratorみたいな感じで使えるのかなと思います。

app/graphql/types/return_gift_type.rb
module Types
  class ReturnGiftType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    field :supplier, Types::SupplierType, null: false

    field :created_at, GraphQL::Types::ISO8601DateTime, null: false
    field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
  end
end

Query

サンプルデータから特定の自治体に紐づく返礼品事業者、返礼品すべてを取得したいと思います。

定義

Query定義、Resolver作成をしていきます。

app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject
    field :local_government, LocalGovernmentType, null: false, description: '自治体情報' do
      argument :id, Int, '自治体ID', required: true
    end
    def local_government(id:)
      LocalGovernment.find_by(id: id)
    end
  end
end

自治体情報はIDで条件指定をできるようにしておきます。

実行

query {
  localGovernment(id: 1){
    id
    suppliers{
      id
      returnGifts{
        id
        name
      }
    }
  }
}

上記のクエリを実行すると、下記のレスポンスが返却されることがわかります。

{
  "data": {
    "localGovernment": {
      "id": "1",
      "suppliers": [
        {
          "id": "1",
          "name": "返礼品事業者X",
          "returnGifts": [
            {
              "id": "1",
              "name": "返礼品A"
            },
            {
              "id": "4",
              "name": "返礼品B"
            },
            {
              "id": "5",
              "name": "返礼品C"
            }
          ]
        },
        {
          "id": "5",
          "name": "返礼品事業者Y",
          "returnGifts": [
            {
              "id": "11",
              "name": "返礼品I"
            },
            {
              "id": "12",
              "name": "返礼品J"
            }
          ]
        }
      ]
    }
  }
}

関連情報も上手く取得できています。

Mutation

次は特定の返礼品事業者の返礼品情報を追加したいと思います。
流れとしては、Mutationクラス作成→Mutation定義作成→クエリ実行になります。

Mutationクラス

app/graphql/mutations/create_return_gift_mutation.rb
module Mutations
  class CreateReturnGiftMutation < Mutations::BaseMutation
    argument :supplier_id, Int, required: true
    argument :name, String, required: true

    field :return_gift, Types::ReturnGiftType, null: true
    field :errors, [String], null: false

    def resolve(supplier_id:, name:)
      return_gift = ReturnGift.new(
        supplier_id: supplier_id,
        name: name
      )

      if return_gift.save
        {
          return_gift: return_gift,
          errors: []
        }
      else
        {
          return_gift: nil,
          errors: return_gift.errors.full_messages
        }
      end
    end
  end
end

Mutation定義

app/graphql/mutations/create_return_gift_mutation.rb
module Types
  class MutationType < Types::BaseObject
    field :create_return_gift, mutation: Mutations::CreateReturnGiftMutation
  end
end

実行

mutation {
  createReturnGift(input: {
    supplierId: 5
    name: "追加した返礼品"
  }) 
  { 
    returnGift{
      id
      supplier{
        id
        name
        returnGifts{
          id
          name
        }
      }
    }
  }
}

上記のクエリを実行すると、下記のレスポンスが返却されることがわかります。
返礼品データも作成されています。

{
  "data": {
    "createReturnGift": {
      "returnGift": {
        "id": "24",
        "supplier": {
          "id": "5",
          "name": "返礼品事業者Y",
          "returnGifts": [
            {
              "id": "11",
              "name": "返礼品I"
            },
            {
              "id": "12",
              "name": "返礼品J"
            },
            {
              "id": "24",
              "name": "追加した返礼品"
            }
          ]
        }
      }
    }
  }
}

おわりに

今回はGraphQLでデータ取得、データ作成をやってみました。
意識せずにドキュメントが作られる点(Graphiql便利すぎ)や単一エンドポイントな点、(正しく命名できていれば)クエリが直感的でわかりやすい点など、結構感動しました。
他にもgraphql-batchを使った場合や型定義など、ベストプラクティスを学んでいきたいと思います。

Hameeアドベントカレンダー2020お疲れさまでした!来年もまたやりましょおおお

(あと、来年度控除のふるさと納税は12/31になってます。まだ間に合うのでやってない方はぜひ。)
https://yummy.hamee-furusato.jp/

REF

graphql-ruby.org
【Rails】graphql-rubyでAPIを作成
GraphQL Ruby の使い方 (基礎編)
Ruby on RailsエンジニアがGraphQLを使うと簡単に最強のWebAPIを作れる話。(デモもあるよ!)

5
0
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
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?