GraphQL是API的查詢語言,不依賴任何前端後臺技術。在服務器端有很多種實現。
本例使用的是graphql-ruby(rails) ,用來解析傳入的查詢,並進行數據庫調用,響應返回JSON,替換rails的api。
首先了解下graphql的幾個組成部分
- Queries:從API獲取特定的數據。將查詢設置爲只讀,就像Rest裏面的Get,但是查詢不僅是Get。
- Mutations: 突變,對API數據的修改,比如:create、update、destroy。
- Types: 類型,用於定義數據類型,或者定義Rails模型model,類型包括根據Queries/Mutations中的請求相應數據的字段和函數。類型也可以是靜態的,比如:String或ID,這些內置在服務器的數據庫中。
- Fields: 字段,表示給定類型的屬性
- 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創建了第一個模型; 不需要額外的路由,控制器或序列化器。更重要的是,我們只從新創建的模型中準確返回了所需的數據。