Rails+GraphiQL을 사용하여 API 만들기
각 버전
ruby: 2.7.1
rails: 6.0.3.4
graphql-ruby: 1.11.6
GraphQL Ruby
공식 페이지
Rails를 사용하여 GraphiQL을 처리할 때 ↑의gem을 사용하여 API를 설치합니다.
graphiql-rails
같이graphiql-railsgem를 미리 넣으면 브라우저에 설치된GraphQL의
IDE:sparkles:
※
graphql-ruby
의 설치시 graphiql-rails
의gem는 Gemfile에 추가이미지
환경 구조
Gemfile
gem 'graphql'
gem 'graphiql-rails' # 今回は先に入れました
gem를 설치한 후 rails generate graphql:install
명령을 실행하여 파일을 생성합니다.생성된 파일은 다음과 같다↓
$ rails generate graphql:install
create app/graphql/types
create app/graphql/types/.keep
create app/graphql/app_schema.rb
create app/graphql/types/base_object.rb
create app/graphql/types/base_argument.rb
create app/graphql/types/base_field.rb
create app/graphql/types/base_enum.rb
create app/graphql/types/base_input_object.rb
create app/graphql/types/base_interface.rb
create app/graphql/types/base_scalar.rb
create app/graphql/types/base_union.rb
create app/graphql/types/query_type.rb
add_root_type query
create app/graphql/mutations
create app/graphql/mutations/.keep
create app/graphql/mutations/base_mutation.rb
create app/graphql/types/mutation_type.rb
add_root_type mutation
create app/controllers/graphql_controller.rb
route post "/graphql", to: "graphql#execute"
gemfile graphiql-rails
route graphiql-rails
이때routes.rb
는 다음과 같다.Rails.application.routes.draw do
# GraphQL
if Rails.env.development?
mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql'
end
post '/graphql', to: 'graphql#execute'
end
이루어지다
Query 만들기
먼저 테이블에 해당하는 Type을 정의해야 합니다.
예를 들어 다음과 같은
users
표에 대응하는user_type
을 만들어 보고 싶습니다.create_table :users do |t|
t.string :name, null: false
t.string :email
t.timestamps
end
다음 명령을 실행하면 user_type
가 생성됩니다.(지정된 유형은
ID
GraphiQL에서 정의한 id용 유형(실제 상황은 String)또한 어미대
!
는 null을 허용하지 않는 유형이고, !
는 null을 허용하지 않는 유형이다.)$ bundle exec rails g graphql:object User id:ID! name:String! email:String
[보충] DB에 테이블이 있다면 잘 부탁드립니다.$ bundle exec rails g graphql:object User
↑이래도 괜찮다:sparkles:생성된 파일
graphql/type/user_type.rb
은 다음과 같습니다.module Types
class UserType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: false
field :email, String, null: true
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
이미 생성된 graphql/type/query_type.rb
에 다음과 같은 내용을 추가한다. field :users, [Types::UserType], null: false
def users
User.all
end
http://localhost:3000/graphiql
에 아래 조회를 던지면 답장이 있을 거라고 생각합니다.{
users {
id
name
email
}
}
Mutation 만들기
다음은 제작자의 뮤타ation스
CreateUser
를 만들고 싶다.$ bundle exec rails g graphql:mutation CreateUser
graphql/mutations/create_user.rb
가 만들어졌으며 다음과 같이 수정되었습니다.module Mutations
class CreateUser < BaseMutation
field :user, Types::UserType, null: true
argument :name, String, required: true
argument :email, String, required: false
def resolve(**args)
user = User.create!(args)
{
user: user
}
end
end
end
이미 생성된graphql/types/mutation_type.rb
에 다음과 같은 내용을 보충한다.module Types
class MutationType < Types::BaseObject
field :createUser, mutation: Mutations::CreateUser # 追記
end
end
http://localhost:3000/graphiql
에서 다음 작업을 수행하면 User가 생성됩니다.mutation {
createUser(
input:{
name: "user"
email: "[email protected]"
}
){
user {
id
name
email
}
}
}
Association
Post
와Label
1:1로 연관된 경우label_type.rb
module Types
class LabelType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: false
...
end
end
module Types
class PostType < Types::BaseObject
field :label, LabelType, null: true
end
end
는 위에서 말한 바와 같이 label
를 LabelType
로 정의할 수 있다.이 경우 Query 이미지로
{
posts {
id
label {
id
name
}
}
}
위에서 설명한 대로 label
를 LabelType
에 필요한 값Query로 사용할 수 있습니다.User
는Post
와1:N의 경우module Types
class PostType < Types::BaseObject
field :id, ID, null: false
field :label, LabelType, null: true
end
end
module Types
class UserType < Types::BaseObject
field :posts, [PostType], null: false
end
end
는 위에서 말한 바와 같이 posts
를[PostType]
라고 정의하고Query로 할 수 있다.{
user(id: 1234) {
id
posts {
id
label {
id
name
}
}
}
}
위와 같이 호출할 수 있습니다.graphql-batch
위에서 설명한 바와 같이 1:1과 1:N의 관련 표의 데이터도 가져올 수 있다
이렇게 되면 DB에 문의가 쏟아질 수 있다.
User
Post
와 1:N의 경우Post
가 100건이면 각각 100건의 문의가 발생한다.그럼 해결 방법 중 하나인 여러 상담graphql-batch을 소개해 드리겠습니다.
gem 'graphql-batch'
젬을 설치한 후 제작loader
.loader
는'다수의 자문을 종합하는'부분의 실현이다.graphql/loaders/record_loader.rb
module Loaders
class RecordLoader < GraphQL::Batch::Loader
def initialize(model)
@model = model
end
def perform(ids)
@model.where(id: ids).each { |record| fulfill(record.id, record) }
ids.each { |id| fulfill(id, nil) unless fulfilled?(id) }
end
end
end
이전 Post
과 Label
1:1로 연결된 경우module Types
class PostType < Types::BaseObject
field :label, LabelType, null: true
def label
Loaders::RecordLoader.for(Label).load(object.label_id)
end
end
end
이렇게 써도 돼요.User
Post
와 1:N일 경우 로더를 별도로 제작한다.graphql/loaders/association_loader.rb
module Loaders
class AssociationLoader < GraphQL::Batch::Loader
def self.validate(model, association_name)
new(model, association_name)
nil
end
def initialize(model, association_name)
@model = model
@association_name = association_name
validate
end
def load(record)
raise TypeError, "#{@model} loader can't load association for #{record.class}" unless record.is_a?(@model)
return Promise.resolve(read_association(record)) if association_loaded?(record)
super
end
# We want to load the associations on all records, even if they have the same id
def cache_key(record)
record.object_id
end
def perform(records)
preload_association(records)
records.each { |record| fulfill(record, read_association(record)) }
end
private
def validate
unless @model.reflect_on_association(@association_name)
raise ArgumentError, "No association #{@association_name} on #{@model}"
end
end
def preload_association(records)
::ActiveRecord::Associations::Preloader.new.preload(records, @association_name)
end
def read_association(record)
record.public_send(@association_name)
end
def association_loaded?(record)
record.association(@association_name).loaded?
end
end
end
※ loader는graphiql-batch의 창고에 샘플이 있으니 이 샘플을 참고하여 설치할 수 있습니다다음과 같이 쓰면 통일적으로 묻는다.
module Types
class UserType < Types::BaseObject
field :posts, [PostType], null: false
def posts
Loaders::AssociationLoader.for(User, :posts).load(object)
end
end
end
모드 파일에서 문서 생성
마지막으로 정의된 패턴 파일에서 감각이 좋은 문서를 자동으로 만들어 보려고 합니다.
routes.rb
에 마운트할 수 있으며, 디버깅할 때마다graphidoc를 자동으로 업데이트합니다편리한gem를 찾을 때graphdoc-ruby라는gem가 있는데 한번 해 볼게요.
Gemfile
에 다음을 추가합니다.gem 'graphdoc-ruby'
또한npm봉인@2fd/graphdoc이 필요합니다.Docker 이미지에 미리 설치합니다.(Docker를 사용하지 않으면 로컬 환경에 설치하면 됩니다.)
예제)
RUN set -ex \
&& wget -qO- https://deb.nodesource.com/setup_10.x | bash - \
&& apt-get update \
&& apt-get install -y \
...
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& npm install -g yarn \
&& npm install -g @2fd/graphdoc # インストールしとく
config/routes.rb
에 다음과 같은 내용을 보충한다.config/routes.rb
Rails.application.routes.draw do
mount GraphdocRuby::Application, at: 'graphdoc'
end
※ 노드 변경 시 수정config/initializers/graphdoc.rb
예제)GraphdocRuby.configure do |config|
config.endpoint = 'http://0.0.0.0:3000/api/v1/graphql'
end
Rails를 다시 시작하고 http://localhost:3000/graphdoc에 문서를 생성하면 OK:sparkles:방독 기술
http://localhost:3000/graphiql
액세스 시 다음 오류가 발생한 경우Sprockets::Rails::Helper::AssetNotPrecompiled in GraphiQL::Rails::Editors#show
해결 방법 1
app/assets/config/manifest.js
에 다음을 추가합니다.//= link graphiql/rails/application.css
//= link graphiql/rails/application.js
AssetNotPrecompiled error with Sprockets 4.0 · Issue #75 · rmosolgo/graphiql-rails -> 이것만 있으면 Production 시
Sprockets::FileNotFound: couldn't find file 'graphiql/rails/application.css'
에 오류가 발생하여 사용할 수 없습니다...해결 방법 2 (순조로운 방법)
gem 'sprocket'のバージョン3.7.2に下げる
gem 'sprockets', '~> 3.7.2' [#1098: slowdev/knowledge/ios/FirebaseをCarthageで追加する](/posts/1098)
↑bundle update
추가Rails6의 API 모드에서 GraphiQL을 사용하는 방법(오류 대책 포함) - Qita
graphiql 화면에 표시
TypeError: Cannot read property 'types' of undefined
->손 옆 환경이라면 Rails를 재부팅하면 좋을 것 같아요.graphiql 화면에 표시
SyntaxError: Unexpected token < in JSON at position 0
-> 오류가 발생할 수 있습니다. 로그를 보고 수정하십시오.참조 링크
Reference
이 문제에 관하여(Rails+GraphiQL을 사용하여 API 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/slowhand/articles/4fe99377185100텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)