こんにちは!
気持ちは永遠の新卒1年目、いつの間にか3年目の株式会社エイチームコマーステック北村 (@NamedPython )です。
今日は、コマーステックがもしかしたら辿るかもしれない技術スタックを先行して検証していきます。
試す技術はタイトルにもある通り「Rails × Hasura」です。
役割分担としては、
-
Rails(API mode)
- マイグレーション
- ビジネスロジック
-
Hasura
- GraphQL Engine
- 単純なCRUD
- ゲートウェイ
- スケジューラー
- GraphQL Engine
みたいな感じにしたいな〜と想像しています。
シンプルなCRUDのGraphQLはHasuraにお任せ、複雑な処理はActionsでRailsに流したり、リゾルバを書いてRemote SchemasでHasuraに取り込んだりしてもいい。Railsでリゾルバを書くのが面倒なら別のバックエンドを立ててしまえばいい。これが実現できると、分厚くなりがちなRailsを徐々にマイクロにしていきつつ、他のスタックに移行するなり、Railsのアップグレードや改善に取り組める。という妄想をしているのです。
本当はMySQLなRails × HasuraのMySQL-previewな構成で試したかったんですが、IssueやDiscussionを見るにHasura社内の開発的にもMySQL対応の難易度が高いようで、公開されているもの↓は実用レベルになかったのでPostgreSQL構成で書いています。
環境をつくっていく
それでは早速作っていきましょう。
以下のような構成を作ります。
rails-hasura/ <- current, project dir
├ hasura
│ └ config.yaml
├ rails
│ ├ Dockerfile
│ ├ Gemfile
│ └ Gemfile.lock
└ docker-compose.yaml
./rails/Gemfile
、./rails/Dockerfile
、./docker-compose.yaml
、は以下です。
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem 'rails', '~> 6.1.4', '>= 6.1.4.4'
FROM ruby:2.6.5
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
nodejs \
default-mysql-client \
yarn
WORKDIR /rails-app
COPY Gemfile Gemfile.lock /rails-app/
RUN bundle install
version: '3.8'
services:
rails:
build:
dockerfile: Dockerfile
context: ./rails
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
ports:
- 3000:3000
volumes:
- ./rails:/rails-app
tty: true
stdin_open: true
hasura:
image: hasura/graphql-engine:v2.1.1.cli-migrations-v3
ports:
- 8080:8080
depends_on:
- db
restart: always
environment:
HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:password@db:5432/postgres
HASURA_GRAPHQL_ENABLE_TELEMETRY: 'false'
HASURA_GRAPHQL_ENABLE_CONSOLE: 'true'
HASURA_GRAPHQL_DEV_MODE: 'true'
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
HASURA_GRAPHQL_UNAUTHORIZED_ROLE: anonymous
HASURA_GRAPHQL_ADMIN_SECRET: devenvsecret
db:
image: postgres:12
restart: always
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: password
volumes:
db_data:
続いて./hasura/config.yaml
です。
version: 3
endpoint: http://localhost:8080/
admin_secret: devenvsecret
enable_telemetry: false
api_paths:
v1_query: v1/query
v2_query: v2/query
v1_metadata: v1/metadata
graphql: v1/graphql
config: v1alpha1/config
pg_dump: v1alpha1/pg_dump
version: v1/version
metadata_directory: metadata
migrations_directory: migrations
seeds_directory: seeds
actions:
kind: synchronous
handler_webhook_baseurl: http://localhost:3000
codegen:
framework: ''
output_dir: ''
さて、ここまで来たらあとはrails new
の儀式です。
docker compose run rails rails new . --force --database=postgresql --skip-webpack-install
そうすると、ガーっとファイルが生成され、bundle install
なども実行されるので待ちます。Gemfile
, Gemfile.lock
の中身も書き変わりますよ。
続いては、./rails/config/database.yml
をいじる必要があるので、お好みのエディタで開き、次のように編集します。
# PostgreSQL. Versions 9.3 and up are supported.
#
# Install the pg driver:
# gem install pg
# On macOS with Homebrew:
# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On macOS with MacPorts:
# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
# On Windows:
# gem install pg
# Choose the win32 build.
# Install PostgreSQL and put its /bin directory on your path.
#
# Configure Using Gemfile
# gem 'pg'
#
default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+ host: db
+ port: 5432
+ username: postgres
+ password: password
そうしましたらいよいよ起動でございます。
docker compose up --build -d
# 起動ログを見たい方は
docker compose logs -f
こちらのコマンド実行もちょっとかかると思うので猫でも撫でてください
Railsからのマイグレーションをしていく
...とその前に個人的な好みと、現行開発の方針的に顧客の目に触れそうな識別子はUUIDv4にしているので、Railsでそれができるようにしていく。
余談: RailsでUUIDv4なプライマリキー
この手順を踏むと一瞬でPostgreSQLに依存するマイグレーションが走ったりしちゃうので注意されたし。
こちらの記事を参考にさせていただきました。インターネットに感謝
手順書こうかと思いましたが、ほぼ記事ままなので割愛。以下からは適用済みの前提があることをご認識ください。
rails g model
-> rails db:migrate
エイチームコマーステックですし、EC的にありきたりなテーブル構造を組みつつ、それがHasuraからどう認識されるかみてみましょう。一度に複数商品注文できるようにすると面倒なので、1注文1商品で。
- お客さん
customers
- 製品
products
- 注文
customer_orders
- 買ったお客さん
customer_orders
.customer_id
->customers
.id
- 買った商品
customer_orders
.product_id
->products
.id
- 買ったお客さん
docker compose exec rails bin/rails g model customer
docker compose exec rails bin/rails g model product
docker compose exec rails bin/rails g model customer_order
./rails/db/migrate/
配下にできたそれぞれのファイルを以下のように編集!
class CreateCustomers < ActiveRecord::Migration[6.1]
def change
create_table :customers, id: :uuid do |t|
t.string :name, null: false
t.string :email, null: false
t.timestamps
end
end
end
class CreateProducts < ActiveRecord::Migration[6.1]
def change
create_table :products, id: :bigint do |t|
t.string :label_name, null: false
t.integer :price, null: false, comment: 'tax excluded'
t.string :description
t.timestamps
end
end
end
class CreateCustomerOrders < ActiveRecord::Migration[6.1]
def change
create_table :customer_orders, id: :uuid do |t|
t.string :order_code, null: false
t.datetime :sold_at, null: false, default: -> { 'CURRENT_TIMESTAMP' }
t.references :customer, type: :uuid, foreign_key: true
t.references :product, foreign_key: true
t.integer :price, null: false
t.integer :price_with_tax, null: false
t.timestamps
end
end
end
そしていよいよマイグレーション実行〜
docker compose exec rails bin/rails db:migrate
Hasuraで確認
さて続いてHasuraでどう認識されているかみてみましょう!
一旦見るだけなので http://localhost:8080/ から見ましょう。
おや、Enter admin-secret
とありますね。大丈夫、まだ慌てる時間じゃありません。
./docker-compose.yml
のhasura
サービス内の環境変数にHASURA_GRAPHQL_ADMIN_SECRET
として設定されている値を入力すればOKです(今回はdevenvsecret
)。
続いての画面はGraphiQLですね。ここも使うんですが今見たいのは画像内のDATA
タブです。
そして次もまたよくわからん画面が出るとは思いますが、続いてはpublic
スキーマをポチー
そうすると、こういった表示になるかと思います。
おや、赤枠内をみるとcustomer_orders
やcustomers
、products
が見えますね。
他も見えてしまっていますが、これはActiveRecord
のためのメタデータ保管庫でしょう。
Untracked tables or views
とのことなので、まあHasura的に未追跡なんでしょう。
Track
のボタンを押してしまいたくなりますが、永続化できなくなってしまうのでReal consoleを立ち上げます。
Hasura CLIのインストールとReal consoleの立ち上げ
公式ドキュメントに従い、Hasura CLIをインストールしてください。筆者はHomebrewで入れました。
続いてこんな感じでコマンドをぽちぽち。
hasura --project hasura console
すると、規定のブラウザで勝手にReal consoleが開きます。
「え、何が違うん?」と思うかもしれませんが、僕も見た目の違いはわかりません
まあポートが違うのでそこで見分けてください。
このコンソールは、さっきの:8080
なコンソールと違い、スキーマや設定に影響が出る変更をすると、設定ファイルとして吐き出され、永続化できます。
Hasuraでtrack、metadataの設定
さて、これで永続化される状態になったので、./hasura/
下に吐かれるファイルをみつつテーブルをTrack
したり、リレーションを定義していきましょう。
テーブルのtrack
さて、Hasuraにテーブルを認識してもらうために、Track
しましょう。
さっきと同じ手順で、DATAタブ > publicスキーマのところまでいきましょう。そして、見覚えのあった3つのテーブルのTrack
ボタンを押していきます。
まずはcustomers
テーブル....
おお〜〜〜〜〜〜〜〜〜〜色々読み込まれとる〜〜〜〜〜〜〜〜!
そしてファイルはどんな感じで吐かれているかというと...?
め、めちゃくちゃ増えてる...
そしてよく見てみると、今回Track
したcustomers
テーブルの名前がついたファイルがありますね(./hasura/metadata/databases/default/tables/public_customers.yaml
)。
中身はどんな感じかというと...?
table:
name: customers
schema: public
おっ...、うん...。割とシンプルな感じですね。
まあTrack
しただけならこんなもんなので、残りのテーブルもTrack
しちゃいましょう。
左のツリーにはテーブルが増え、なにやら別種のUntracked ...
も増えてきました。
どうやらこれは、外部キー制約をもとに判断した「これrelationships貼ったほうがいいんじゃないの?」というHasuraのレコメンデーションみたいです。
ざっと見ただけでも、
- お客さんは注文をいっぱい持ってる
customers
->[ customer_orders ]
- 製品はいっぱい注文されている
products
->[ customer_orders ]
- 注文したお客さんを辿れる
customer_orders
->customers
- 注文された製品を辿れる
customer_orders
->products
と列挙されているので、いい感じですね。賢いッ!
リレーションのtrack
せっかく上記のようにレコメンドが出ていますし、全部良さそうなのでTrack All
しちゃいましょう。
alert(...)
で確認され、OKするとそのままレコメンドだけが消えて、完了のtipsが出ますね。
ファイルの変更を確認してみましょう。
table:
name: customers
schema: public
+ array_relationships:
+ - name: customer_orders
+ using:
+ foreign_key_constraint_on:
+ column: customer_id
+ table:
+ name: customer_orders
+ schema: public
お〜、やはりリレーションが追加されるのもちゃんとファイルに吐かれますね。
テストデータを入れてGraphiQLで読み書きする
さて、いよいよ下準備整ってきたので、ラストの検証します。
まずはテストデータですね。こんなところでしょうか。
-
customers
-
name
: NamedPython -
email
: namedpython@namedpython.local -
allowed_to_send_emails
: true
-
-
products
-
label_name
: ゆずシトラスティー -
price
: 390 -
description
: パッションティーカスタムが美味しい
-
-
customer_orders
-
order_code
: NAMED-ORDERCODE-123 -
customer_id
: ↑ -
product_id
: ↑ -
price
: 390 -
price_with_tax
: 429
-
テストコードは、Rails
consoleでもいいですし、Hasura
consoleでもできます。
後者でやるなら以下のような感じで入力画面にたどり着けます!
僕もこのタイミングで気づいたんですが、Rails製のcreated_at, updated_atはDB上にデフォルト値が設定されるわけではなくしかもNULL不可なので、後者でやる場合はcreated_at, updated_atもnow()で埋めてあげる必要があります。
ここはRails側のデフォルトのマイグレーション設定を書き換えてあげた方が良さそうなポイントですね。
テストデータを投入できたら早速クエリしてみましょう!
これまで触ってきたDATAタブの左側、APIタブを開きましょう!
いつの間にか、ここにもいろいろできていますね。
では単純なところから行ってみましょうか。
query ShowProductInformations {
products {
id
label_name
price
description
}
}
お〜取得できましたね〜!
そしたらこういうのはどうですかね?
query ShowCustomersProfileAndOrders {
customers {
id
name
email
customer_orders {
order_code
price_with_tax
product {
label_name
price
}
}
}
}
お〜、has_many / has_oneな関係もちゃんとGraphQLでとれてますね!!!!
さて、GraphQLがいい感じに叩けるのがわかったところで、今回の検証は以上にしようと思います。
実は他にもEnum
だったりComputed Fields
だったりの検証もしたかったんですが、それはまた別の機会に
おわりに + We're hiring!
ここまで読んでいただいてありがとうございました!
株式会社エイチームコマーステックには、僕みたいな新しいモノ好きがこういったことに挑戦できる土壌があり、そういった環境で「Eコマースを面白くしていきたいぜ」という方を募集しています
「ちょっとお話聞いてみたいぞ!」だったり、「Rails/Hasuraどんな感じで使ってるんだ?」が気になる方は、Twitter(@NamedPython)のDMやリプライまでお気軽にお声がけください!
コマーステックのコーポレートサイトや、採用ページもチェックしてみてくださいね!
それでは良い年末年始をお過ごしください!