記事の内容
Graphqlをキャッチアップするにあたって、そもそもなんでGraphqlを使用するのか? からはじめました。
また、学習するにあたって初めて聞くような単語がたくさんあったので、メモがてらこの記事にまとめていこうと思いました。
この記事ではDockerでの環境構築からuserのindex,show,createまでを記載しようと思います。
Graphqlとは?
さっそくですが参考がとてもわかりやすかったのです。GraphQLを導入した有名なサービスも紹介されており面白いです。
API用のクエリ言語であり、REST APIではサーバ側で返すデータ構造等を決めていたが、実際にデータを使用するクライアント側で自由にデータを取得できます。
今日のスマホやSPAでは複雑なデータを扱うことが多いため、注目されている。
メリット・デメリット
メリット
- フロント側でレスポンスの形式を指定できる
- 必要なデータのみ取得でき、オーバーフェッチ、アンダーフェッチがなくなる
- Swagger等の仕様書が必要なくなる
- 型があるため堅牢
- 一度に複数のリソースを取得できる
- RESTでいう post/1,post/2のようなリソースを一気に取得できる
デメリット
- 学習コストがかかる
- キャッシュが REST よりも複雑になる。N+1問題等に気をつける必要あり。
単語
- クエリ言語:Graphqlサーバへの問い合わせ内容を記載した言語
- スキーマ言語: Graphqlサーバでのデータの型を記載した言語
- リゾルバ: 問い合わせを実行するメソッド
- Query: SQLでいうSELECTにあたるもの。データを取得する際に使用
- Mutation: SQLでいうINSERT, UPDATE,DELETE。データを作成、変更削除する際に使用
- field: クラスメソッド。データをDBからもってくる単位
- Argument: 検索やデータの呼び出しに使う引数。Queryに渡す引数を指定。
実装
環境構築
1.各種ファイルを用意
以下ファイルを作成
FROM ruby:3.1.2
RUN mkdir /app
WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY . /app
# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
EXPOSE 3306
# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
version: '3'
services:
db:
platform: linux/x86_64 #M1対応
image: mysql:5.7
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: root
MYSQL_USER: root
MYSQL_PASSWORD: root
ports:
- '3306:3306'
volumes:
- ./tmp/db:/var/lib/mysql
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/app
ports:
- '3000:3000'
depends_on:
- db
source 'https://rubygems.org'
gem 'rails', '~>7.0.3'
空のGemfile.lock
#!/bin/bash
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /app/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
2.Rails new
% docker-compose run --rm web rails new . --force --database=mysql --api
3.イメージbuild
% docker-compose build
4.database.ymlの設定
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: password
host: db
5.DBのcreate,migration
% docker-compose run --rm web rails db:create
Creating rails_backend_web_run ... done
Created database 'app_development'
Created database 'app_test'
% docker-compose run --rm web rails db:migration
6.コンテナ起動
% docker-compose up -d
http://localhost:3000/ にアクセスし、Railsの画面がでればOK
Graphql
参考
graphql-rubyでAPIを作成
Rails + GraphQLでAPI作成
準備
graphqlのインストール
gem 'graphql' #追加
% bundle install
% rails generate graphql:install #様々なファイルが作成されます
上記コマンドで記述されているか確認する
Rails.application.routes.draw do
post "/graphql", to: "graphql#execute" # rails generate graphql:installで記述されているか確認
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
# root "articles#index"
end
動作確認
Altair GraphQL Clientをインストールして、下記のようにリクエストを送信しHello World!が表示されればOK。
モデル作成
% rails g model user
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
% rails db:migrate
型ファイルの作成
% rails g graphql:object User
create app/graphql/types/user_type.rb
上記コマンドを実行し、先程作成したモデルに合わせて以下ファイルが生成されます。
# frozen_string_literal: true
module Types
class UserType < Types::BaseObject
field :id, ID, null: false
field :name, String
field :email, String
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
Queryの作成
Query: SQLでいうSELECTにあたるもの。データを取得する際に使用。
graphql/type/query_type.rb に下記のように記述します。
module Types
class QueryType < Types::BaseObject
# Add `node(id: ID!) and `nodes(ids: [ID!]!)`
include GraphQL::Types::Relay::HasNodeField
include GraphQL::Types::Relay::HasNodesField
# Add root-level fields here.
# They will be entry points for queries on your schema.
field :users, resolver: Resolvers::QueryTypes::UsersResolver # 追加
# fieeld: クラスメソッド。データをDBからもってくる単位
# 追加した記述でリゾルバ(問い合わせを実行するメソッド)を実行
end
end
app/graphql/resolvers/query_types/users_resolver.rbに下記のように記述
module Resolvers::QueryTypes
class UsersResolver < GraphQL::Schema::Resolver
type [Types::UserType], null: false
def resolve
User.all
end
end
end
画像のような結果になっていればOK(Userのレコードは各自作成してください)
Argumentを使用し、指定ユーザのデータを取得
Argument: 検索やデータの呼び出しに使う引数。Queryに渡す引数を指定。
graphql/type/query_type.rb にfieldを追加。
module Types
class QueryType < Types::BaseObject
include GraphQL::Types::Relay::HasNodeField
include GraphQL::Types::Relay::HasNodesField
# Add root-level fields here.
# They will be entry points for queries on your schema.
field :users, resolver: Resolvers::QueryTypes::UsersResolver
field :user, resolver: Resolvers::QueryTypes::UserResolver # 追加
# fieeld: クラスメソッド。データをDBからもってくる単位
# 追加した記述でリゾルバ(問い合わせを実行するメソッド)を実行
end
end
app/graphql/resolvers/query_types/users_resolver.rbファイルを作成し、以下のように記述。
module Resolvers::QueryTypes
class UserResolver < GraphQL::Schema::Resolver
type Types::UserType, null: false
argument :id, ID, required: false # 引数。
# 型はapp/graphql/resolvers/query_types/users_resolver.rbを参照
def resolve(id:)
User.find(id)
end
end
end
以下のようなクエリで指定したIDのユーザ情報を取得
query {
user(id: 1) {
id
name
email
createdAt #ここに記載したカラムのデータが返ってくる
}
}
また、nameで検索したい場合は以下のように修正
module Resolvers::QueryTypes
class UserResolver < GraphQL::Schema::Resolver
type Types::UserType, null: false
argument :name, String, required: false # 引数。name
# 型はapp/graphql/resolvers/query_types/users_resolver.rbを参照
def resolve(name:)
User.find_by(name: name)
end
end
end
query {
user(name: "test2") {
id
name
email
createdAt
}
}
結果
Mutationの作成
Mutation: SQLでいうINSERT, UPDATE,DELETE。データを作成、変更削除する際に使用。
まずはユーザ作成を実装します。
下記コマンドを実行します。
% rails g graphql:mutation CreateUser
下記ファイルが作成されます。
module Types
class MutationType < Types::BaseObject
field :create_user, mutation: Mutations::CreateUser
end
end
同じく自動生成された app/graphql/mutations/create_user.rbを下記のように修正。
module Mutations
class CreateUser < BaseMutation
graphql_name 'CreateUser'
field :user, Types::UserType, null: true
argument :name, String, required: true # 引数
argument :email, String, required: true # 引数
def resolve(**args) # **argsに引数がす
user = User.create!(args)
{
user: user
}
end
end
end
以下のmutationを実行
mutation {
createUser(
input:{
name: "user"
email: "user@email.com"
}
){
user {
id
name
email
}
}
}
argsの中身は以下のようになっています
[1] pry(#<Mutations::CreateUser>)> args
=> {:name=>"user", :email=>"user@email.com"}
結果
終わりに
環境構築から簡単な機能の実装方法をざっとまとめてみました。
今後graphql-railsの設計も学習したいと思います。