Rails使用GraphQL

GraphQL是API的查詢語言,不依賴任何前端後臺技術。在服務器端有很多種實現。

本例使用的是graphql-ruby(rails) ,用來解析傳入的查詢,並進行數據庫調用,響應返回JSON,替換rails的api。

 

首先了解下graphql的幾個組成部分

  1. Queries:從API獲取特定的數據。將查詢設置爲只讀,就像Rest裏面的Get,但是查詢不僅是Get。
  2. Mutations: 突變,對API數據的修改,比如:create、update、destroy。
  3. Types: 類型,用於定義數據類型,或者定義Rails模型model,類型包括根據Queries/Mutations中的請求相應數據的字段和函數。類型也可以是靜態的,比如:String或ID,這些內置在服務器的數據庫中。
  4. Fields: 字段,表示給定類型的屬性
  5. Functions: 方法、功能,給上面的字段提供數據,

上面的5個部分配合工作,完成各種API的功能。

創建RailsAPI

創建一個rails api的項目來體驗下graphql。

rails  new graphql_api  --api
rails g model User email:string name:string
rails g model Book title:stirng  summary:string  user:belongs_to
rails db:migrate

在user.rb裏面加入 has_many :books

可以通過faker創建一些數據,比如:

5.times do
  user = User.create(name: Faker::Name.name, email: Faker::Internet.email)
  5.times do
    user.books.create(title: Faker::Book.title)
  end
end

rake db:seed

 

生成GraphQL文件

 

rails generate graphql:install
bundle
rails generate graphql:object user
rails generate graphql:object book

創建graphql目錄和2個新的自定義類型User和Book

  ├─ controllers
+ │  └─ graphql_controller.rb
+ ├─ graphql
+ │  ├─ mutations
+ │  ├─ rails_graphql_demo_schema.rb
+ │  └─ types
+ │     ─ base_enum.rb
+ │     ─ base_input_object.rb
+ │     ─ base_interface.rb
+ │     ─ base_object.rb
+ │     ─ base_scalar.rb
+ │     ─ base_union.rb
+ │     ─ book_type.rb
+ │     ─ mutation_type.rb
+ │     ─ query_type.rb
+ │     ─ user_type.rb

同時也會創建app/controllers/graphql_controller.rb#execute,作爲api訪問的入口,開發環境增加一個graphiql-rails的gem包

# routes.rb

Rails.application.routes.draw do
  if Rails.env.development?
    mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "graphql#execute"
  end
  post "/graphql", to: "graphql#execute"
end

現在啓動項目,訪問http://localhost:3000/graphiql,就可以看到測試api接口的頁面

 

我們還要創意一系列類型Type,便於GraphQL知道,GraphQL的類型(Tyep)等同於API的Model,也要指定字段(Fields)、方法(functions)等用於返回到客戶端的應用程序。

User和Book的Type

每個字段都有個‘類型’以及是否允許爲null,告訴graphql對傳入和傳出的數據的要求,便於傳給前端和後端正確的數據。

:id, :name這些字段會對應之前創建的User模型中的字段。

這裏定義了一個自定義的字段:books_count, Rails模型中並不存在,因此我們將其定義在字段列表的下方,

自定義的方法在models's  scope,可以通過self.books.size調用。

# app/graphql/types/user_type.rb
module Types
  class UserType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: true
    field :email, String, null: true
    field :books, [Types::BookType], null: true
    field :books_count, Integer, null: true

    def books_count
      books.size
    end
  end
end


# app/graphql/types/book_type.rb
module Types
  class BookType < Types::BaseObject
    field :title, String, null: false
  end
end

主查詢類型

query_type.rb和mutation_type.rb這兩種傳入請求的路由,定義在模式schema裏面,他們和Rails路由和資源有些相似。

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

在query_type文件裏面,定義:users和:user的字段,以及他們的方法,

users方法返回UserType類型的一組對象。

user方法接收一個類型爲ID的:id的參數,返回一個UserType對象,(ID是一個內置的類型)

# app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject

    field :users, [Types::UserType], null: false

    def users
      User.all
    end

    field :user, Types::UserType, null: false do
      argument :id, ID, required: true
    end

    def user(id:)
      User.find(id)
    end
  end
end

查詢字段

訪問http://localhost:3000/graphiql, 在您的瀏覽器上粘貼users的查詢代碼,我們在上面添加了查詢字段。在這裏,我們確切地指定了我們希望API響應的內容; 在這種情況下,我們只需要一個用戶名,電子郵件列表和他們擁有的書籍數量。

query {
  users {
    name
    email
    booksCount
  }
}

還可以查詢單個用戶相關信息:

query {
  user(id: 1) {
    name
    email
    books {
      title
    }
  }
}

Mutations(突變)

Mutations允許創建、修改、銷燬數據,我們設置一個基類,用來擴展CreateUser的mutation。

# app/graphql/mutations/base_mutation.rb
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
end

Arguments參數:在這裏我們指定接受哪些參數作爲參數,以及它們是什麼對象類型, 這是必需的。這有點類似於在Rails控制器中定義強參數,這裏對進入的內容進行更細粒度的控制。

Fields字段:和上面查詢字段概念相同,接收參數創建對象,同時希望返回一個user帶有我們的新模型的字段,並附帶一個errors數組。

Resolver解析器:resolve方法是執行ActiveRecord命令的地方。他返回一個帶有和上面定義的字段名稱一樣的鍵的hash.

# app/graphql/mutations/create_user.rb
class Mutations::CreateUser < Mutations::BaseMutation
  argument :name, String, required: true
  argument :email, String, required: true

  field :user, Types::UserType, null: false
  field :errors, [String], null: false

  def resolve(name:, email:)
    user = User.new(name: name, email: email)
    if user.save
      # Successful creation, return the created object with no errors
      {
        user: user,
        errors: [],
      }
    else
      # Failed save, return the errors to the client
      {
        user: nil,
        errors: user.errors.full_messages
      }
    end
  end
end

最後,將新突變mutation添加到主突變類型類中,以便它暴露給我們的API。

# app/graphql/types/mutation_type.rb
module Types
  class MutationType < Types::BaseObject
    field :create_user, mutation: Mutations::CreateUser
  end
end

創建用戶

測試,請打開http://localhost:3000/graphiql並粘貼以下查詢。

注意我們傳入一個createUser(input: {})對象; 這映射到:create_user接受單個input參數的字段。在graphql-ruby的文檔中瞭解有關此設計的更多信息。

mutation {
  createUser(input: {
    name: "Matt Boldt",
    email: "[email protected]"
  }) {
    user {
      id
      name
      email
    }
    errors
  }
}

成功!我們剛剛通過GraphQL創建了第一個模型; 不需要額外的路由,控制器或序列化器。更重要的是,我們只從新創建的模型中準確返回了所需的數據。

 

來源:https://mattboldt.com/2019/01/07/rails-and-graphql/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章