概要
DataLoaderを使うためにgqlgenにカスタムモデルを導入した際、
gqlgen
でpanic: interface conversion
エラーが発生したので、その解決の記録を残します。
DataLoaderってなんやねん って方は以下がわかりやすいです。
簡単にいうと、N+1問題を避けるためにバッチ処理を使って効率的にデータを取得しようってことみたいです。
おことわり
- 筆者はBE1年目の駆け出しGopherです。
- 表現に曖昧な箇所や不正確な箇所があるかもしれませんがご容赦ください。
ハマった内容
グラフモデルの生成
schemaを以下のように記述し、gqlgenしました。
type User {
id: ID!
name: String!
department: Department!
}
type Department {
# 省略
}
type Query {
users: [User!]!
}
input NewUser {
name: String!
deparmentId: ID!
}
type Mutation {
createUser(input: NewUser!): User!
}
以下のようなmodelが生成されます。
package graph
type User struct {
ID uint64 `json:"id"`
Name string `json:"name"`
Department Department `json:"department"`
}
type Department struct {
// 省略
}
type NewUser struct {
Name string `json:"name"`
DepartmentID uint64 `json:"departmentId"`
}
カスタムモデルの導入
User
をバルクで取得した際のN+1問題を避けるために、DataLoaderを導入したいです。
データローダーのためにカスタムモデルを使用します。
カスタムモデルについては以下を参照してください。
まずは先ほど生成したグラフモデルからUser
を削除します。
package graph
- type User struct {
- ID uint64 `json:"id"`
- Name string `json:"name"`
- Department Department `json:"department"`
- }
type Department struct {
// 省略
}
type NewUser struct {
Name string `json:"name"`
DepartmentID uint64 `json:"departmentId"`
}
上で削除したUser
をモデルのファイルに宣言します。
また、ドメインオブジェクト生成のためのコンストラクタの定義も行いました。
package model
import (
// 省略
)
type User struct {
ID uint64 `json:"id"`
Name string `json:"name"`
DepartmentID uint64 `json:"departmentId"` // dataloaderのためidを指定
}
type UserDescription struct {
Name string
DepartmentID uint64
}
// Userのコンストラクタを定義
func NewUser(desc UserDescription) *User {
return &User{
Name: desc.Name,
DepartmentID: desc.DepartmentID,
}
}
gqlgenを再度実行
以下のエラーが出てしまいました。
go1.21.1 run github.com/99designs/gqlgen@v0.17.39 generate --verbose
panic: interface conversion: types.Type is *types.Signature, not *types.Named
# 以下省略
解決法
こちらのコメントが参考になりました。
GQLタイプと同名の関数が存在していたことでエラーが発生していたようです。
package graph
type NewUser struct {
Name string `json:"name"`
DepartmentID uint64 `json:"departmentId"`
}
package model
// 省略
// Userのコンストラクタを定義
func NewUser(desc UserDescription) *User {
return &User{
Name: desc.Name,
DepartmentID: desc.DepartmentID,
}
}
スキーマを以下のように変更して重複をなくすと、エラーが発生しなくなりました。
type User {
id: ID!
name: String!
department: Department!
}
type Department {
# 省略
}
type Query {
users: [User!]!
}
- input NewUser {
+ input UserCreateInput {
name: String!
deparmentId: ID!
}
type Mutation {
- createUser(input: NewUser!): User!
+ createUser(input: UserCreateInput!): User!
}
感想
一度気づくと「なぜ気づかなかったのだろう」となりますが、エラーメッセージからだと結構気づきづらいなと感じました。(小並感)