Ruby on Rails의 GraphQL 다중 스키마 생성

29036 단어 rubyrailsgraphql
Nesse는 Ruby on Rails에서 GraphQL의 일반 스키마를 사용하여 대부분의 문제를 해결하고, 보석을 사용하기 위해 응용 프로그램의 품질을 확인하는 부분을 선택했습니다graphql.

GraphQL은 스키마를 분리하기 위한 동기를 부여하고 자동 재귀를 필요로 하며 자동으로 종료해야 합니다. 필요한 경우 별도의 os 스키마가 필요한 경우 응용 프로그램을 실행하기 위해 응용 프로그램을 실행하고 시스템 내부 관리를 실행해야 합니다. Assim, seus schemas entregam apenas o que é necessário para aplicação que irá consumir e também restringe o acesso de recursos internos.

손에!



Todo o código fonte pode ser encontrado aqui

Neste example, vamos fazer uma API de um blog, mas para manter a simplicidade, vamos ignorar a parte de autenticação.

Teremos dois 스키마.
  • Schema público que trará a lista de posts e seus revivos autores.
  • Schema privado que permitirá cadastrar novos posts.

  • Após criar a plicação, install a gem graphql e executar o comando rails generate graphql:install, teremos a seguinte estrutura de arquivos.

    app
    ├── controllers
    │   ├── application_controller.rb
    │   └── graphql_controller.rb
    └── graphql
        ├── app_name_schema.rb
        ├── mutations
        │   └── base_mutation.rb
        └── types
            ├── base_argument.rb
            ├── base_connection.rb
            ├── base_edge.rb
            ├── base_enum.rb
            ├── base_field.rb
            ├── base_input_object.rb
            ├── base_interface.rb
            ├── base_object.rb
            ├── base_scalar.rb
            ├── base_union.rb
            ├── mutation_type.rb
            ├── node_type.rb
            └── query_type.rb
    


    네임스페이스, 쿼리, 뮤테이션, 유형과 같은 파스타 그래프를 정확히 살펴보세요.

    Veja como é o schema inicial gerado pelo installador.

    # app/graphql/app_name_schema.rb
    class AppNameSchema < GraphQL::Schema
      mutation(Types::MutationType)
      query(Types::QueryType)
    end
    


    Um 스키마는 돌연변이로 기본 클래스를 구성하고 클래스는 쿼리로 쿼리를 정의합니다.

    Veja como é o controller inicial gerado pelo installador.

    class GraphqlController < ApplicationController
      protect_from_forgery with: :null_session
    
      def execute
        variables = prepare_variables(params[:variables])
        query = params[:query]
        operation_name = params[:operationName]
        context = {
          # Query context goes here, for example:
          # current_user: current_user,
        }
        result = AppNameSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
        render json: result
      rescue StandardError => e
        raise e unless Rails.env.development?
        handle_error_in_development(e)
      end
    
    private
    
      # Handle variables in form data, JSON body, or a blank value
      def prepare_variables(variables_param)
        case variables_param
        when String
          if variables_param.present?
            JSON.parse(variables_param) || {}
          else
            {}
          end
        when Hash
          variables_param
        when ActionController::Parameters
          variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables.
        when nil
          {}
        else
          raise ArgumentError, "Unexpected parameter: #{variables_param}"
        end
      end
    
      def handle_error_in_development(e)
        logger.error e.message
        logger.error e.backtrace.join("\n")
    
        render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500
      end
    end
    


    Precisaremos definir dois endpoints graphql, um para o schema público e outro para o schema privado.

    Mas primeiro, vamos gerar os models que usaremos na aplicação.

    rails g model author name
    rails g model post title content:text author:references
    rails g model email email
    


    O 모델은 이메일을 통해 게시물을 수신하거나 공개적으로 사용하는 방법을 사용합니다.

    Vamos는 pelo graphql público를 제공합니다. Vamos ter 1 query que lista os posts, 1 query que retorna um post pelo ID e uma mutation que cadastra o email na lista de emails.

    파스타 두 graphql público fica assim.

    # app/graphql/public_graphql.rb
    class PublicGraphql < GraphQL::Schema
      mutation(Mutations)
      query(Queries)
    end
    
    # app/graphql/public_graphql/mutations.rb
    class PublicGraphql
      class Mutations < Types::BaseObject
        field :add_email_to_list, mutation: AddEmailToList
      end
    end
    
    # app/graphql/public_graphql/queries.rb
    class PublicGraphql
      class Queries < Types::BaseObject
        include Posts
        include PostById
      end
    end
    
    


    Acima já descrevi as query e mutations que teremos, elas ficarão dentro dos namespaces Queries e Mutations.

    쿼리와 같은 seguir.

    # app/graphql/public_graphql/queries/posts.rb
    class PublicGraphql
      class Queries
        module Posts
          extend ActiveSupport::Concern
    
          included do
            field :posts, [Types::Post], null: false
          end
    
          def posts
            Post.all
          end
        end
      end
    end
    
    # app/graphql/public_graphql/queries/post_by_id.rb
    class PublicGraphql
      class Queries
        module PostById
          extend ActiveSupport::Concern
    
          included do
            field :post_by_id, Types::Post, null: true do
              argument :id, GraphQL::Types::ID, required: true
            end
          end
    
          def post_by_id(id:)
            Post.find_by(id:)
          end
        end
      end
    end
    


    Para que as query funcionem, precisamos dos types que utilizamos nelas, que é apenas o Post .

    # app/graphql/types/post.rb
    module Types
      class Post < Types::BaseObject
        field :id, ID, null: false
        field :title, String, null: false
        field :content, String, null: false
        field :author, Types::Author, null: false
      end
    end
    
    # app/graphql/types/author.rb
    module Types
      class Author < Types::BaseObject
        field :id, ID, null: false
        field :name, String, null: false
      end
    end
    


    참고 que colocamos os 유형 na 파스타 raiz 유형 mesmo. Fiz isso pois precisamos desses tipos no graphql privado também, então aqui fica compartilhado. 특정 스키마에 대한 특정 스키마를 사용하는 경우 PublicGraphql 메모 공간에서 네임스페이스를 사용할 수 있습니다.

    Já as mutations, movi a classe Mutations::BaseMutation (essa é a que foi gerada automaticamente pela gem) para Types::BaseMutation . 돌연변이 파스타를 제외하고 파스타를 만드십시오.

    돌연변이 AddEmailToList fica assim:

    # app/graphql/public_graphql/mutations/add_email_to_list.rb
    class PublicGraphql
      class Mutations
        class AddEmailToList < Types::BaseMutation
          argument :email, String, required: true
    
          field :success, Boolean, null: false
    
          def resolve(email:)
            { success: !!Email.create(email:) }
          end
        end
      end
    end
    


    Para finalizar o Graphql público, falta seu controller. Assim, PublicGraphqlController의 크리아모스

    # app/controllers/public_graphql_controller
    class PublicGraphqlController < ApplicationController
      def execute
        variables = prepare_variables(params[:variables])
        query = params[:query]
        operation_name = params[:operationName]
        result = PublicGraphql.execute(query, variables:, operation_name:) # here we call the PublicGraphql class
        render json: result
      rescue StandardError => e
        raise e unless Rails.env.development?
        handle_error_in_development(e)
      end
    
      private
    
      # same private methods from the generated class
    end
    


    Não esqueça de atualizar as rotas

    Rails.application.routes.draw do
      post "/graphql", to: "public_graphql#execute"
      # ...
    end
    
    


    O PrivateGraphql podemos seguir o mesmo estilo. Aqui vamos ter a listagem de posts de um autor e o cadastro de posts.

    # app/graphql/private_graphql.rb
    class PrivateGraphql < GraphQL::Schema
      mutation(Mutations)
      query(Queries)
    end
    
    # app/graphql/private_graphql/queries.rb
    class PrivateGraphql
      class Queries < Types::BaseObject
        include MyPosts
      end
    end
    
    # app/graphql/private_graphql/mutations.rb
    class PrivateGraphql
      class Mutations < Types::BaseObject
        field :create_post, mutation: CreatePost
      end
    end
    
    # app/graphql/private_graphql/queries/my_posts.rb
    class PrivateGraphql
      class Queries
        module MyPosts
          extend ActiveSupport::Concern
    
          included do
            field :my_posts, [Types::Post], null: false
          end
    
          def my_posts
            context[:current_author].posts
          end
        end
      end
    end
    
    # app/graphql/private_graphql/mutations/create_post.rb
    class PrivateGraphql
      class Mutations
        class CreatePost < Types::BaseMutation
          argument :title, String, required: true
          argument :content, String, required: true
    
          field :post, Types::Post, null: false
    
          def resolve(title:, content:)
            post = Post.new(title:, content:, author: context[:current_author])
            if post.save
              {post:}
            else
              {}
            end
          end
        end
      end
    end
    
    # app/controllers/private_graphql_controller.rb
    class PrivateGraphqlController < ApplicationController
      # here you can put an authentication method to protect your private graphql schema
      # before_action :authenticate! 
    
      def execute
        variables = prepare_variables(params[:variables])
        query = params[:query]
        operation_name = params[:operationName]
        context = { current_author: Author.first } # hardcoding an author just for example
        result = PrivateGraphql.execute(query, variables:, context:, operation_name:)
        render json: result
      rescue StandardError => e
        raise e unless Rails.env.development?
        handle_error_in_development(e)
      end
    
      private
    
      # same private methods from the generated class
    end
    
    # config/routes.rb
    Rails.application.routes.draw do
      # ...
      post "/private_graphql", to: "private_graphql#execute" # added
    end
    


    빨리! Temos dois schemas bem separados e organizados.
    Nossa nova estrutura ficou da seguinte forma.

    ├── private_graphql
    │   ├── mutations
    │   │   └── create_post.rb
    │   ├── mutations.rb
    │   ├── queries
    │   │   └── my_posts.rb
    │   └── queries.rb
    ├── private_graphql.rb
    ├── public_graphql
    │   ├── mutations
    │   │   └── add_email_to_list.rb
    │   ├── mutations.rb
    │   ├── queries
    │   │   ├── post_by_id.rb
    │   │   └── posts.rb
    │   └── queries.rb
    ├── public_graphql.rb
    └── types
        ├── author.rb
        ├── base_argument.rb
        ├── base_connection.rb
        ├── base_edge.rb
        ├── base_enum.rb
        ├── base_field.rb
        ├── base_input_object.rb
        ├── base_interface.rb
        ├── base_mutation.rb
        ├── base_object.rb
        ├── base_scalar.rb
        ├── base_union.rb
        ├── mutation_type.rb
        ├── node_type.rb
        ├── post.rb
        └── query_type.rb
    


    Nessa organização fica simples encontrar onde cada operação está e só de bater o olho na 파스타 você consegue ver tudo o que aquela API expõe.

    Vale notar também que separar cada query e cada mutation em um único arquivo também ajuda na hora dos testes, pois eles ficam mais concisos!

    조직이 필요합니까? 여러 조직 스키마 graphql에 대해 어떻게 생각하십니까? Deixa teu 코멘트 abaixo :) !

    좋은 웹페이지 즐겨찾기