patorashのブログ

方向性はまだない

ActiveRecordのmodel名が〜Typeで終わるものをgraphql-rubyで扱う

なかなか情報がなかったので、書いときます。

model名が◯◯Typeで終わることって、ままあるのかなと思います。CompanyモデルとCompanyTypeモデルのような感じで。これをGraphQLでTypeを定義しようとすると、Types::CompanyTypeTypes::CompanyTypeTypeになってしまいます。読みにくすぎる…。

後ろにTypeがつかないといかんのか?っていうところも疑問だったのですが、別にそんなルールもないみたいでした。 GraphQL - Scalars をみていたら、class Types::Url < Types::BaseScalarとやっていたので。

また、デフォルトのディレクトリ構造のままだとtypesディレクトリに大量のファイルが作られて見辛くなってきたので、ディレクトリを分けました。

blog.spacemarket.com

ここを参考に、ほぼ同様の構成にしました。

app/graphql/enum_types/
           /input_types/
           /interface_types/
           /mutations/
           /object_types/
           /scalar_types/
           /types/
           /union_types/

ディレクトリ名を名前空間として、ファイル名をクラス名にした構成にしないといけないため、ファイルの移動後はリネーム祭りに。 Types::FooTypeObjectTypes::Fooのように修正です。

これで、Types::CompanyTypeObjectTypes::Companyへ、Types::CompanyTypeTypeObjectTypes::CompanyTypeへと分かりやすく変更できてよかった!と思っていたのですが、これだけだと動きません。

query {
  company(id: 1) {
    id
    name
    companyType {
      id
      name
    }
  }
}

上記のようなクエリを発行しようとすると、エラーで落ちました。

{
  "error": {
    "message": "Duplicate type definition found for name 'Company' at 'Field Company.companyType's return type' (ObjectTypes::Company, ObjectTypes::CompanyType)",
    "backtrace": [
      # 略

Companyで定義されているcompanyTypeに当てはまるTypeが重複してるぞ!って言われてます。

module ObjectTypes
  class Company < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    field :company_type, ObjectTypes::CompanyType, null: false # ここ
  end
end

いやいや、ObjectTypes::CompanyTypeって指定してるじゃん!って思うんですが、メタプログラミングしてるんでしょう。多分、別名をつけるメソッドとかあるだろう…と思ってググるDSL形式の頃のgraphql-rubyのコードだったけれど、name = 'CompanyType'みたいなことをしているページを発見。

Shinosaka.rb #27 (GraphQL) に参加した - @znz blog

CompanyTypeのほうに、雑にname 'CompanyType'を追加してみます。

module ObjectTypes
  class CompanyType < Types::BaseObject
    name 'CompanyType' # 雑に追加

    field :id, ID, null: false
    field :name, String, null: false
  end
end

その後、適当にクエリを投げてみると、エラーメッセージが変わりました。

{
  "error": {
    "message": "The new name override method is `graphql_name`, not `name`. Usage: graphql_name \"CompanyType\"",
    "backtrace": [
      # 略

graphql_nameメソッドで別名を定義しろと。

module ObjectTypes
  class CompanyType < Types::BaseObject
    graphql_name 'CompanyType' # 修正

    field :id, ID, null: false
    field :name, String, null: false
  end
end

すると、エラーが消え、ちゃんとcompany(id: 1)に紐づくCompanyTypeの情報が取得できました😀

追記:rails cで検証

検証のために、該当箇所をコメントアウトしてみます。

module ObjectTypes
  class CompanyType < Types::BaseObject
    # graphql_name 'CompanyType' # コメントアウト

    field :id, ID, null: false
    field :name, String, null: false
  end
end

rails cして確認します。

$ bin/rails c
Loading development environment (Rails 5.2.2.1)

Frame number: 0/16
[1] pry(main)> ObjectTypes::CompanyType.graphql_name
=> "Company"
[2] pry(main)> ObjectTypes::Company.graphql_name
=> "Company"

CompanyTypeのgrqphql_nameはCompanyとなってしまい、重複してますね。その後、コメントアウトを解除して再度rails cしてみます。

$ bin/rails c
Loading development environment (Rails 5.2.2.1)

Frame number: 0/16
[1] pry(main)> ObjectTypes::CompanyType.graphql_name
=> "CompanyType"
[2] pry(main)> ObjectTypes::Company.graphql_name
=> "Company"

重複しなくなったのでOK!