Ruby on RailsのサービスをNext.js+GraphQLに置き換えした話
はじめに
こんにちは!マイベストで入社してから2年目になるtkeita1024 です。現在は、Frontendのエンジニアをしています!
今回は、新卒で入社してから1年かけてRuby on Railsで表示されているページをNext.js + GraphQLへ置き換えした話について書きたいと思います。
背景
マイベストでは、2022年~2023年にグローバル化対応というプロジェクトでRuby on Railsで表示されているページをNext.js + React + TypeScriptにし、APIをGraphQLを使用するように一部置き換えました。入社した当時は、主要なページのみしか置き換えが完了しておらず、管理画面(Admin)や新規事業(Favlist)の画面の置き換えは完了していない状況でした。そこで、新卒のタスクとして、置き換えが完了していない画面を全て置き換えるというのを取り組むことになりました。
どのように進めたか
また、GraphQLの実装とNext.js + React + TypeScriptの実装をそれぞれバックエンドエンジニアとフロントエンドエンジニアで分担しながら進めていきました。
置き換えを行うために、以下の手順で取り組んでいきました。
- 置き換えられていないページ・APIの調査
- GraphQLのスキーマを定義
- GraphQLのQuery・Mutationの実装
- SlimファイルをReact + TypeScriptで実装
- GraphQLとの繋ぎ込み
- 不要なコードの削除
1.置き換えられていないページ・APIの調査
はじめに、置き換えられているものと置き換えられていないものが混在しているため、置き換えるべき対象を明確にするため調査を行いました。調査では、Railsのルーティングやコントローラーなどのファイル、実装されている画面のコードなどを全て手作業で確認して、置き換えすべきかどうかを調べました。
また、進捗の可視化を行うために、調べたものを以下のようなNotionにまとめて取り組みました。
2.GraphQLのスキーマを定義
マイベストでは、Client Firstの設計思想のもとスキーマを決めています。
この思想は主にフロントエンドで使用したいFieldを元に、スキーマを設計するということを意味しています。
そのため、フロントエンドで必要なFieldを相談して、ObjectTypesに定義していました。
module Admin
module ObjectTypes
class UserType < Types::BaseObject
field :id, ID, null: true
field :name, String, null: true
field :email, String, null: true
end
end
end
また、フロントエンドで日付や価格表示の情報を表示したい場合は、基本的にバックエンド側で整形したものをFieldとしてフロンエンドに渡すようにしています。この理由としては、フロントエンド内のコードを複雑にしないようにするためです。
module Admin
module ObjectTypes
class ItemType < Types::BaseObject
field :id, ID, null: true
field :name, String, null: true
field :price, Int, null: true
field :formatted_price, String, null: true
def formatted_price
... # 変換する処理
end
end
end
end
3.GraphQLのQuery・Mutationの実装
スキーマを定義したらQuery・Mutationの実装を行いました。
基本的には、Controllerのindex・show・new・editアクションはQueryで実装するように、create・update・destroyアクションはMutationで実装するようにしていました。
class UsersController < ApplicationController
# Queryで実装
def index
@users = User.all
end
# Mutationで実装
def create
@user = User.new(user_params)
if @user.save
redirect_to @user, notice: 'ユーザーが作成されました。'
else
render :new
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password)
end
end
上記のようなControllerを置き換える場合は、indexアクションをQueryで、createアクションをMutationで実装し、以下のように書きます。
# Queryの実装
module Resolvers
class UsersResolvers < BaseResolver
type Types::UserType.connection_type, null: false
def resolve
User
end
end
end
# Mutationの実装
module Mutations
class CreateUserMutation < BaseMutation
argument :id, ID, required: true
argument :email, String, required: true
argument :password, String, required: true
field :user, Types::UserType, null: true
field :errors, [String], null: true
def resolve(name:, email:)
user = User.new(name: name, email: email
if user.save
{ user: user }
else
{ errors: user.errors.full_messages }
end
end
end
end
4.SlimファイルをReact + TypeScriptで実装
マイベストでは、RailsのviewにRubyのテンプレートエンジンであるslimを使用しています。
そのため、slimファイルで書かれている内容をReact + TypeScriptに置き換える必要があります。
置き換える際には、slimファイルの実装を元に、React + TypeScriptに置き換えても影響が出ないように実装していきました。
ルーティングに関しては、Next.jsのルーティングを使用しました。
マイベストでは、以下のようにpagesを親ディレクトリとしてルーティングを設定しています。
また、*.page.tsx
をルーティングとして表示するように設定し、呼び出すGraphQLのQueryやFieldの設定は*.graphql
で定義するようにしています。
pages/
└── admin/
└── user/
├── index.page.tsx
├── index.graphql
└── [id]/
└── edit/
├── index.page.tsx
└── index.graphql
5.GraphQLとの繋ぎ込み
マイベストでは、繋ぎ込みにApollo Clientを使用しています。
以下のようなクエリを呼ぶためにgraphqlファイルでクエリや呼び出したいFieldを定義するようにしています。
# UserQuery
query user {
user {
id
name
email
}
}
# CreateUserMutation
mutation user($id: ID, $name: string, $email: string) {
createUser(id: $id, name: $name, email: $email) {
errors
}
}
これらを定義したものをGraphQL Code GeneratorでuseQuery
やuseMutation
のhook
・型を自動生成させます。その後使用したいファイルで使用したいクエリをimportして、以下のように使用することでGraphQLとの繋ぎ込みが可能です。
const { data, error, loading, refetch } = useUserQuery();
const [createUser] = useCreateUserMutation({
variables: {
id
name
email
}
});
6.不要なコードの削除
置き換えた後は、Railsにある、ControllerやView、ルーティングのファイル・コードの削除を行います。
削除を行う際には、削除した後もNext.jsに置き換えた画面に影響が出ないことを確認してから削除します。
取り組んで学んだこと
バックエンドエンジニアとフロントエンドエンジニアがそれぞれ1人ずつで、1年かけて置き換えを行なっていきました。それぞれがバックエンドの実装とフロントエンドの実装を分担しながら取り組んできましたが、スキーマの定義は上手くいかないことがありました。例えば、Client Firstをもとにスキーマを定義してそれを分担して実装した際に、バックエンド側の構造的に良くないことがわかり、スキーマを再設計することになりました。そのため、大きな手戻りが発生してしまいました。
その後は、フロントエンド・バックエンドでそれぞれ実装した場合のProsConsをDesignDocにまとめました。それらの中から一番最適なものはどれか決めて実装しました。以下のようにNotionでDesignDocを作成して決めました。
今後は、バックエンドエンジニアとフロントエンドエンジニアが互いにこのスキーマなら実装できそうかを相談しながらできると良さそうかと思いました。
まとめ
今回は、新卒1年目でNext.js+GraphQLに置き換えした話について紹介しました。今回の置き換えで多くの学びなどがあったので、新卒2年目ではこの学びを活かしていきたいと思います。
株式会社マイベストのテックブログです! 採用情報はこちら > notion.so/mybestcom/mybest-information-for-Engineers-8beadd9c91ef4dc2b21171d48a4b0c49
Discussion