Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เริ่มต้น GraphQL ใน Ruby: Practical Intro

คุณอาจเคยได้ยินนักพัฒนาฟังดัง GraphQL ใน series นี้ เราชอบเรียนรู้เทคโนโลยีโดยการ使用它 และบทความนี้จะผ่านตัวอย่าง application ที่ใช้ GraphQL

What is GraphQL

GraphQL 是一個 query language 和 runtime 可用於建立 APIs 它佔據 development stack 中與 REST API 相似的位置但更靈活 與 REST 不同 GraphQL 允許 response formats 和 content 被 client 指定 就像 SQL SELECT statements 允許指定查詢結果 GraphQL 允許指定返回的 JSON data structures 遵循 SQL analogy GraphQL 不提供 WHERE clause 但識別應用程序對象上的字段應為 response 提供數據

GraphQL 正如其名 建模 APIs 彷彿應用程序是一個 data graph 雖然這描述可能不是你如何看待你的應用程序 但它是大多數系統使用的模型 可以用 JSON 表示的數據是一個 graph 因為 JSON 只是一個 directed graph 將應用程序視為通過 API 呈現 graph model 將使 GraphQL 更容易理解

Using GraphQL in an Application

現在我們在抽象層面描述了 GraphQL 讓我們實際建立一個使用 GraphQL 的 application 從定義 data model 或 graph 開始 去年我採用了一個新愛好 我正在學習彈 electric upright bass 以及一般音樂 所以當想出 demo app 時 音樂相關的例子浮現在腦海

例子中的 object types 是 Artist 和 Song Artists 有多個 Song 和一個 Song 關聯一個 Artist 每個 object type 有 attributes 如 name

Define the API

GraphQL 使用 SDL (Schema Definition Language) 有時在 GraphQL specification 中稱為 "type system definition language" GraphQL types 理論上可以用任何語言定義但最常見的 agnostic language 是 SDL 所以讓我們用 SDL 定義 API

type Artist {
  name: String!
  songs: [Song]
  origin: [String]
}

type Song {
  name: String!
  artist: Artist
  duration: Int
  release: String
}

一個 Artist 有 name 是 String 驚嘆號意味著 field 是 non-null songs 是 Song objects 的陣列 和 origin 是 String 陣列 Song 類似但有一個奇特的 field release field 應該是 time 或 date type 但 GraphQL 沒有將該類型定義為 core type 為了 complete portability 不同 GraphQL implementations 之間 使用 String 我們將使用的 GraphQL implementation 有 Time type 添加 所以讓我們改變 Song 定義使 release field 成為 Time type 返回值將是 String 但通過將類型設為 Time 我們更準確地 document API

release: Time

最後一步是描述如何獲取一個或多個對象 這稱為 root 或對於 queries 是 query root 我們的 root 將只有一個 field 或 method 叫 artist 並要求 artist name

type Query {
  artist(name: String!): Artist
}

Writing the Application

讓我們看看如何在 application 中使用這個 有幾個 GraphQL server implementations for Ruby 一些 approaches 要求上面的 SDL 被翻譯成 Ruby equivalent Agoo 一個我建立的 HTTP server 使用 SDL definition 原樣 和 Ruby code 是 plain vanilla Ruby 所以這就是我們將使用的

注意 Ruby classes 匹配 GraphQL types 通過讓 Ruby class names 匹配 GraphQL type names 我們不添加任何不必要的 complexity

class Artist
  attr_reader :name
  attr_reader :songs
  attr_reader :origin

  def initialize(name, origin)
    @name = name
    @songs = []
    @origin = origin
  end

  # Only used by the Song to add itself to the artist.
  def add_song(song)
    @songs << song
  end
end

class Song
  attr_reader :name # string
  attr_reader :artist # reference
  attr_reader :duration # integer
  attr_reader :release # time

  def initialize(name, artist, duration, release)
    @name = name
    @artist = artist
    @duration = duration
    @release = release
    artist.add_song(self)
  end
end

Methods 匹配 Ruby classes 中的 fields 注意方法要么沒有參數要么 args={} 這是 GraphQL APIs 期望的 我們的 implementation 在這裡遵循 suit initialize methods 用於為例子設置數據 正如我們將看到的

Query root class 也需要被定義 注意 artist 方法匹配 SDL Query root type attr_reader 為 artists 也被添加 那將被 expose 給 API 只需將該 field 添加到 SDL document 中的 Query type

class Query
  attr_reader :artists

  def initialize(artists)
    @artists = artists
  end

  def artist(args={})
    @artists[args['name']]
  end
end

GraphQL root (不要與 query root 混淆) 位於 query root 之上 GraphQL 定義它可選擇擁有三個 fields Ruby class 在這種情況下 implement query field 初始化器載入一些測試數據 為一個我喜歡聽的來自紐西蘭的 Indie band

class Schema
  attr_reader :query
  attr_reader :mutation
  attr_reader :subscription

  def initialize()
    # Set up some data for testing.
    artist = Artist.new('Fazerdaze', ['Morningside', 'Auckland', 'New Zealand'])
    Song.new('Jennifer', artist, 240, Time.utc(2017, 5, 5))
    Song.new('Lucky Girl', artist, 170, Time.utc(2017, 5, 5))
    Song.new('Friends', artist, 194, Time.utc(2017, 5, 5))
    Song.new('Reel', artist, 193, Time.utc(2015, 11, 2))
    @artists = {artist.name => artist}
    @query = Query.new(@artists)
  end
end

最終設置是 implementation specific 在這裡 server 被初始化以包含 /graphql HTTP request path 的 handler 然後啟動

Agoo::Server.init(6464, 'root', thread_count: 1, graphql: '/graphql')
Agoo::Server.start()

GraphQL implementation 然後用之前定義的 SDL ($songs_sdl) 配置 然後 application sleep 而 server processes requests

Agoo::GraphQL.schema(Schema.new) {
  Agoo::GraphQL.load($songs_sdl)
}
sleep

這個例子的代碼可以在 GitHub 上找到

Using the API

為了測試 API 你可以使用 web browser, Postman 或 curl

要嘗試的 GraphQL query 長這樣:

{
  artist(name:"Fazerdaze") {
    name
    songs{
      name
      duration
    }
  }
}

查詢詢問名為 Fazerdaze 的 Artist 並在 JSON document 中返回 name 和 songs 對於每個 Song 返回 Song 的 name 和 duration 在 JSON object 中 輸出應該長這樣

{
  "data": {
    "artist": {
      "name": "Fazerdaze",
      "songs": [
        { "name": "Jennifer", "duration": 240 },
        { "name": "Lucky Girl", "duration": 170 },
        { "name": "Friends", "duration": 194 },
        { "name": "Reel", "duration": 193 }
      ]
    }
  }
}

在去除查詢中的可選空白後 使用 curl 進行 HTTP GET 應該返回該內容

curl -w "\n" 'localhost:6464/graphql?query=\{artist(name:"Fazerdaze")\{name,songs\{name,duration\}\}\}&indent=2'

嘗試更改查詢並將 duration 替換為 release 並注意 Ruby Time 到 JSON string 的轉換

Outro to the song

我們享受玩弄 GraphQL 並希望你跟著學到了一些東西 謝謝 你是個很棒的聽眾 如果你想聊更多 Ruby 我們會在酒吧賣周邊