Rails로 GraphiQL API 만들기 자습서 [graphiql-ruby]

46645 단어 GraphQLRailsRubytech
이 글은 그래픽QL 자체 설명과 그래픽QL 특유의 용어 설명 등을 하지 않는다.
래일스를 이용해 그라피QL의 API를 빠르게 제작하자는 취지로 제작됐다.
2021년 5월 2일 시점의 최신 Version입니다.
ruby: 3.0.1
rails: 6.1.3.1
graphql-ruby: 1.12.8

GraphiQL 설정


API 모드rails new.
minitest를 사용하지 않기 때문에 닫혔습니다.
$ rails new rails-graphql-api -T --api -d postgresql

GraphiQL Ruby 가져오기


Gemfile.rb
# Gemfile
+ gem 'graphql'
$ bundle install
이어서 bin/rails g graphql:install를 실행하고 필요한 파일을 설정합니다.
$ bin/rails g graphql:install
실행 결과
$ bin/rails g graphql:install
Running via Spring preloader in process 50049
      create  app/graphql/types
      create  app/graphql/types/.keep
      create  app/graphql/rails_graphql_api_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"
Skipped graphiql, as this rails project is API only
  You may wish to use GraphiQL.app for development: https://github.com/skevy/graphiql-app
      create  app/graphql/types/node_type.rb
      insert  app/graphql/types/query_type.rb
      create  app/graphql/types/base_connection.rb
      create  app/graphql/types/base_edge.rb
      insert  app/graphql/types/base_object.rb
      insert  app/graphql/types/base_object.rb
      insert  app/graphql/types/base_union.rb
      insert  app/graphql/types/base_union.rb
      insert  app/graphql/types/base_interface.rb
      insert  app/graphql/types/base_interface.rb
      insert  app/graphql/rails_graphql_api_schema.rb

GraphiQL IDE 가져오기


GraphiQL API의 동작을 확인하기 위해 GraphiQL을 추가합니다.
입력 방법에는 응용 프로그램과 Gem 두 가지가 있습니다.
  • GraphiQL.app
  • gem graphiql-rails
  • Gem을 통과하면 Sprockets를 통해 IDE를 실행하지만 API 모드에서는 Sprockets가 적용되지 않으므로 활성화해야 합니다.
    ※ API 모드가 아닌 경우 그래피ql-rails에 가입한 상태bin/rails g graphql:install에서 실행하면 자동으로 설정됩니다.
    IDE에서 Sprockets를 사용하기 위해 미묘하게 프로그램을 설치하는 방법을 선택했습니다.
    GraphiQL은 페이스북제지만 Prism제GraphQL Playground와 Apollo제Apollo Studio 등 효율적으로 GraphiQL을 개발하는 도구가 끊임없이 등장하고 있다.
    GraphiQL만 사용해 봤기 때문에 다른 IDE들도 사용해 보려고 합니다.

    1. GraphiQL 응용 프로그램 설치


    https://github.com/skevy/graphiql-app
    brew cask를 통해 설치합니다.
    $ brew install --cask graphiql
    
    설치가 완료되면 GraphiQL을 엽니다.
    이럴 때는 안전성과 프라이버시가 차단되기 때문에 허용됩니다.
    GraphiQL Endpoint에 http://localhost:3000/graphql를 입력합니다.
    제너레이트가 하면 샘플쿼리로 테스트필드를 준비했기 때문에 실행 가능한지 확인합니다.
    메뉴 모음의▶ 실행에 따르다.
    {
      testField
    }
    

    2. Gem 기반 설치


    Gem 기반 설치 방법
    Rails에 GraphiQL IDE를 사용할 수 있는 graphiql-rails를 추가합니다.
    Gemfile
    gem 'graphiql-rails'
    
    경로/graphiql를 편집하여 GraphiQLroutes.rb을 표시합니다.
    config/routes.rb
    # 以下を追加
    if Rails.env.development?
      mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
    end
    
    이어서 Sprockets를 사용하여 GraphiQL의 화면을 설정합니다.
    API 모드의 경우 Sprockets가 비활성화되어 활성화됩니다.
    Note on API Mode
    If you're using Rails 5 in "API mode", you'll also need to add require "sprockets/railtie"to your application.rb.
    config/application.rb
    -  # require "sprockets/railtie"
    +  require "sprockets/railtie"
    
    sprockets 제작에 필요한 manifest.
    이번에는 개발환경 스프록스만 사용하기 때문에 안은 비어 있어 문제없습니다.
    $ mkdir -p app/assets/config && touch app/assets/config/manifest.js
    
    assets 파일을 만들고 개발 환경에서GraphiQL의assets를 읽습니다.
    $ touch app/initializers/assets.rb
    
    config/initializers/assets.rb
    if Rails.env.development?
      Rails.application.config.assets.precompile += %w[graphiql/rails/application.js
                                                       graphiql/rails/application.css]
    end
    
    
    $ bin/rails s
    
    http://localhost:3000/graphiql 다음 화면을 표시하면 됩니다.

    해보자Query


    모델 생성


    Post 및 Comment 모델을 만듭니다.
    $ bin/rails g model post title:string body:text
    $ bin/rails g model comment post:references body:text
    
    를 각 열에 추가null: false.
    # xxxx_create_posts.rb
    class CreatePosts < ActiveRecord::Migration[6.1]
      def change
        create_table :posts do |t|
          t.string :title, null: false
          t.text :body, null: false
    
          t.timestamps
        end
        add_index :posts, :created_at
      end
    end
    
    # xxxx_create_comments.rb
    class CreateComments < ActiveRecord::Migration[6.1]
      def change
        create_table :comments do |t|
          t.references :post, null: false, foreign_key: true
          t.text :body, null: false
    
          t.timestamps
        end
      end
    end
    

    Object Type 제작


    그런 다음 포스트와 Comment 유형을 만듭니다.
    모델과 같은 이름으로 제작하면 각 열과 유형에 자동으로 덧붙인다.
    has-many 등은 읽을 수 없기 때문에 스스로 설정합니다.bin/rails g graphql:object 型名 カラム名:型에서 생성됩니다.
    $ bin/rails g graphql:object post comments:Comment
    $ bin/rails g graphql:object comment
    
    app/graphql/types/post_type.rb
    module Types
      class PostType < Types::BaseObject
        field :id, ID, null: false
        field :title, String, null: false
        field :body, String, null: false
    -   field :comments, Types::CommentType, null: true
    +   field :comments, [Types::CommentType], null: false
        field :created_at, GraphQL::Types::ISO8601DateTime, null: false
        field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
      end
    end
    
    app/graphql/types/comment_type.rb
    module Types
      class CommentType < Types::BaseObject
        field :id, ID, null: false
    -   field :post_id, Integer, null: false
    +   field :post, Types::PostType, null: false
        field :body, String, null: false
        field :created_at, GraphQL::Types::ISO8601DateTime, null: false
        field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
      end
    end
    

    Query 만들기


    포스트를 가져오는 Query를 query_type.rb에 작성합니다.
    app/graphql/types/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
    
    +   field :post, Types::PostType, null: false do
    +     description 'Find a post by ID'
    +     argument :id, ID, required: true
    +   end
    +
    +   def post(id:)
    +     Post.find(id)
    +   end
      end
    end
    
    
    얻을 수 있는지 없는지를 시험하기 위해 데이터를 만들다.
    $ bin/rails c
    post = Post.create(title: 'test', body: 'body')
    post.comments.create(body: 'comment 1')
    post.comments.create(body: 'comment 2')
    
    GraphiQL을 사용하여Query를 실행합니다.
    query {
      post(id: "1") {
        id
        title
        body
        comments {
          id
          body
        }
      }
    }
    

    Post 및 Post와 연결된 Comments 확보🎉

    Resolver로 바꿔볼게요.

    query_type.rb 점점 비대해지기 때문에 수량이 증가하는 상황에서 또는 복잡한 획득 방법을 통해 Resolver 형태로 분리된다.
    처음부터 Resolver 형식을 자주 사용하는 것도 좋다.
    Resolvers 디렉토리를 생성합니다.
    $ mkdir app/graphql/resolvers
    
    기타 기본 상황에 따라 기본 정보를 작성한다.
    app/graphql/resolvers/base_resolver.rb
    module Resolvers
      class BaseResolver < GraphQL::Schema::Resolver
        argument_class Types::BaseArgument
      end
    end
    
    post_resolver.rb.def resolve ... end 반환할 데이터 처리
    resolve의 매개 변수에argument의 값(여러 개가 있을 경우 여러 개가 들어간다)이 포함되어 있기 때문에 resolve(id:) 또는 resolve(**args)의 형식으로 매개 변수를 받아들인다.
    app/graphql/resolvers/post_resolver.rb
    module Resolvers
      class PostResolver < Resolvers::BaseResolver
        description 'Find a post by ID'
        type Types::PostType, null: false
    
        argument :id, ID, required: true
    
        def resolve(id:)
          Post.find(id)
        end
      end
    end
    
    query_type.rbfield :post를resolver의 형식으로 개작하다.
    app/graphql/types/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
    
    +   field :post, resolver: Resolvers::PostResolver
    -   field :post, Types::PostType, null: false do
    -     description 'Find a post by ID'
    -     argument :id, ID, required: true
    -   end
    -
    -   def post(id:)
    -     Post.find(id)
    -   end
      end
    end
    
    이렇게 하면 Resolver에서 취득하는 것으로 변경할 수 있다.
    GraphiQL에서 같은 Query를 사용하여 얻을 수 있는지 확인하십시오.

    Mutation을 해보도록 하겠습니다.


    Create Post Mutation


    다음은 포스트 메이킹을 위한 뮤테이션을 제작한다.g graphql:mutation로 제작하면 mutation_type.rbcreate_post.rb로 추가 제작됩니다.
    $ bin/rails g graphql:mutation create_post
    
    실행 결과
    $ bin/rails g graphql:mutation create_post
    Running via Spring preloader in process 56616
           exist  app/graphql/mutations
       identical  app/graphql/mutations/.keep
       identical  app/graphql/mutations/base_mutation.rb
            skip  app/graphql/types/mutation_type.rb
    add_root_type  mutation
          create  app/graphql/mutations/create_post.rb
            gsub  app/graphql/types/mutation_type.rb
    
    app/graphql/types/mutation_post.rb
    module Types
      class MutationType < Types::BaseObject
    +   field :create_post, mutation: Mutations::CreatePost
      end
    end
    
    create_post.rb 편집합니다.
    field는 반환 값을 지정합니다.
    resolve의 반환값은field의 반환값에 따라 산열 형식으로 반환됩니다.
    app/graphql/mutations/create_post.rb
    module Mutations
      class CreatePost < BaseMutation
    +   field :post, Types::PostType, null: false
    +
    +   argument :body, String, required: true
    +   argument :title, String, required: true
    +
    +   def resolve(**params)
    +     post = Post.create!(params)
    +     { post: post }
    +   end
      end
    end
    
    GraphiQL로 해보세요.
    mutation ($input: CreatePostInput!) {
      createPost(input: $input) {
        post {
          title
          body
        }
      }
    }
    
    왼쪽 아래에 있는 QUERY VAIABLES를 클릭하면 입력할 수 있습니다.
    variables
    {
      "input": {
        "title": "new title",
        "body": "new body"
      }
    }
    

    새 포스트가 작성됨🎉

    InputObject에서 설치


    arguument는 InputObject를 사용하여 요약하여 공통화할 수 있습니다.
    UpdatePost의 InputObject를 사용해 보십시오.
    inputs 디렉토리를 만듭니다.
    $ mkdir app/graphql/types/inputs
    
    BaseInputObject가 이미 있기 때문에 제작post_input_type.rb했습니다.
    app/graphql/types/inputs/post_input_type.rb
    module Types
      module Inputs
        class PostInputType < Types::BaseInputObject
          argument :id,    Int,    required: true
          argument :body,  String, required: false
          argument :title, String, required: false
        end
      end
    end
    
    이어 제작update_post.rb.
    $ bin/rails g graphql:mutation update_post
    
    app/graphql/types/mutation_post.rb
    module Types
      class MutationType < Types::BaseObject
        field :create_post, mutation: Mutations::CreatePost
    +   field :update_post, mutation: Mutations::UpdatePost
      end
    end
    
    PostInputType을argument에 건네주면params는 PostInputType의 유형을 가질 수 있습니다.
    app/graphql/mutations/update_post.rb
    module Mutations
      class UpdatePost < Mutations::BaseMutation
    +   argument :params, Types::Inputs::PostInputType, required: true
    +
    +   def resolve(params:)
    +     post_params = params.to_h
    +     post = Post.find(post_params.delete(:id))
    +     post.update!(post_params.compact)
    +     post
    +   end
      end
    end
    
    GraphiQL로 실행해 보세요.
    mutation ($params: UpdatePostInput!) {
      updatePost(input: $params) {
        post {
          id
          title
          body
        }
      }
    }
    
    variables
    {
      "input": {
        "params": {
          "id": 1,
          "title": "update title"
        }
      }
    }
    

    포스트 타이틀 변경됨🎉
    InputObject는 공동화하고 싶은argment가 설립될 때 만들 수 있으면 좋겠다고 생각했어요.

    참고 자료


    https://graphql-ruby.org/getting_started
    https://qiita.com/dkawabata/items/4fd965ee6d7295386a8b
    https://github.com/rmosolgo/graphiql-rails/issues/75
    https://zenn.dev/kei178/articles/2f4ffc6b89618c

    좋은 웹페이지 즐겨찾기