Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cookpad Summer Internship 2021 Web API

Cookpad Summer Internship 2021 Web API

Cookpad Summer Internship 2021 10 Day Tech コース3日目講義パート
https://techlife.cookpad.com/entry/2021/09/06/130000

Takahiro Miyoshi

August 18, 2021
Tweet

More Decks by Takahiro Miyoshi

Other Decks in Programming

Transcript

  1. Cookpad Summer Internship 2021 10 Day Tech
    Day 3: Web API
    2021-08-18

    View full-size slide

  2. Image
    Area
    Image
    Area
    Image
    Area
    @sankichi92
    (講師)
    技術部
    ユーザー・決済基盤
    グループ
    @osyoyu
    (TA)
    メディアプロダクト開
    発部
    マーケティングサー
    ビス開発グループ
    @s4ichi
    (TA)
    技術部
    クックパッドサービス
    基盤グループ

    View full-size slide

  3. タイムテーブル
    ● 10:00 講義: 要素技術の解説
    ● 12:00 ランチ休憩
    ● 13:00 ハンズオン
    ● 13:30 課題
    ● 17:30 5つのグループに分かれて成果発表
    ● 17:50 基礎課題の簡単な解説
    ● 18:00 終了

    View full-size slide

  4. minimart API の要素技術
    ● Ruby
    ● Ruby on Rails
    ● GraphQL (Ruby)
    ● gRPC

    View full-size slide

  5. minimart API 概観
    minimart API
    Clients
    (Next.js, iOS)
    minifinancier
    (決済 API を提供)

    View full-size slide

  6. 課題
    https://github.com/cookpad/cookpad-internship-2021-sum
    mer-api
    ● このリポジトリで課題を進めます
    ● docs 以下を読みながら各自で minimart API の実装を進
    めてください

    View full-size slide

  7. アンケート
    Ruby を
    ● 書いたことがない #=> 12
    ● 普段使わないが書いたことはある #=> 6
    ● 普段使いしている #=> 2

    View full-size slide

  8. Ruby の特徴
    ● オブジェクト指向スクリプト言語
    ○ すべてがオブジェクト
    ■ プリミティブ型がない 42.class #=> Integer
    ● 動的型付け
    ○ Ruby 3.0 から静的型解析のための仕組みも
    ■ Ruby 3 の静的解析ツール TypeProf の使い方
    ■ Ruby 3の静的解析機能のRBS、TypeProf、Steep、Sorbetの関係につ
    いてのノート
    ● 非常に柔軟

    View full-size slide

  9. ● レシピやマートなど全社的
    に利用
    ● 2名のフルタイム Ruby コ
    ミッターが在籍
    クックパッドにおける Ruby

    View full-size slide

  10. ローカル変数、リテラル、演算子式など
    # 変数宣言は不要
    price = 42
    tax = 4.2
    # 変数(やメソッド)はスネークケースが慣習
    tax_included_price = price + tax #=> 46.2
    name = "トマト"
    "おいしい#{name}" #=> "おいしいトマト"
    # nil と false 以外は真と評価される
    !!false #=> false
    !!nil #=> false
    !!"" #=> true
    !!0 #=> true
    # 文字列とは別にシンボル (Symbol) があり、連想配列のキーなど識別子として利用される
    :tomato.object_id == :tomato.object_id #=> true
    "tomato".object_id == "tomato".object_id #=> false

    View full-size slide

  11. メソッド定義、メソッド呼び出し
    def hello # 引数のないメソッド
    "Hello, world!" # return がない場合最後の式の値を返すので return は不要
    end
    hello #=> "Hello, world!"
    hello() # カッコをつければメソッド呼び出しであることを明示できる(が、基本は省略する)
    # tax_rate はデフォルト値付きキーワード引数
    def calculate_tax(price, tax_rate: 0.1)
    price * tax_rate
    end
    calculate_tax(100) #=> 10.0
    calculate_tax 100 # 引数のある場合もカッコを省略できる(が、省略するかは場合による)
    calculate_tax 100, tax_rate: 0.08 #=> 8.0

    View full-size slide

  12. 制御構造 (if)
    price = 300
    # if も式であり値を返す( if だけでなくすべては値を持つ)
    price_label = if price > 1000
    :expensive
    elsif price > 100
    :mid_range
    else
    :cheap
    end
    # 後置 if
    puts "not cheap" if price_label != :cheap

    View full-size slide

  13. 配列 (Array)、連想配列 (Hash)
    # 配列
    categories = ["meat", "fish", "vegetables"]
    categories[1] #=> "fish"
    categories.first #=> "meat"
    categories.size #=> 3
    # 文字列がキーの Hash
    item_to_price = { "onion" => 70, "carrot" => 80, "potato" => 50 }
    item_to_price["onion"] #=> 70
    # シンボルがキーの Hash
    item_to_price = { onion: 70, carrot: 80, potato: 50 }
    item_to_price[:carrot] #=> 80
    item_to_price["carrot"] #=> nil

    View full-size slide

  14. ブロック、イテレータ
    # %記法を使った配列
    categories = %w[meat fish vegetables] #=> ["meat", "fish", "vegetables"]
    # do ... end または { ... } で囲まれたコードのかたまり(ブロック)をメソッドに渡せる
    categories.each do |category|
    puts category
    end
    categories.map { |c| c.upcase } #=> ["MEAT", "FISH", "VEGETABLES"]
    # for や while もあるがほとんど使わない
    10.times do
    puts "Hello, world!"
    end

    View full-size slide

  15. クラス
    class Greeter
    def initialize(name) # コンストラクタ
    @name = name # インスタンス変数
    end
    def say_hello
    "hello, #{@name}"
    end
    end
    class LoudGreeter < Greeter # 継承(単一継承のみ)
    def say_hello
    "#{super.upcase}!!!"
    end
    end
    greeter = Greeter.new("world")
    greeter.say_hello #=> "hello, world"
    LoudGreeter.new("world").say_hello #=> "HELLO, WORLD!!!"

    View full-size slide

  16. 定数・モジュール
    MASCOT_NAME = "mini-tomart" # 大文字ではじまる識別子は定数
    # 定数 Greeter に Class クラスのインスタンスを代入
    Greeter = Class.new # class Greeter; end と同じ(クラスもオブジェクト)
    module Minimart # モジュールを使って定数の名前空間を分けている( Mixinについては割愛)
    class Greeter
    def say_hello
    "Hello, #{MASCOT_NAME}"
    end
    end
    end
    greeter = Minimart::Greeter.new # :: 演算子を使ってアクセス
    greeter.say_hello #=> "Hello, mini-tomart!"

    View full-size slide

  17. ライブラリ (gem)
    ● Ruby のサードパーティライブラリは RubyGems.org に gem
    として置かれている
    ● gem install rails で gem をインストール
    ● require "rails" で gem を利用できる
    ○ (ただし、Rails ではアプリケーション初期化時に依存 gem を読み込んだ
    り、定数の自動読み込み仕組みがあったりしていて require を書かなくても
    利用できてしまう)

    View full-size slide

  18. Bundler
    ● gem の依存関係を管理するためのツール bundler.io
    ● Gemfile に利用する gem を記述
    ○ gem "rails", "~> 6.1.4" のようにバージョンを指定できる
    ● bundle install で Gemfile の gem をインストール
    ○ Gemfile.lock がなければ依存関係を解決して Gemfile.lock を作成
    ○ Gemfile.lock があればそこで指定されたバージョンをインストール
    ● bundle exec command で Gemfile で指定された gem
    を利用する形で command (Ruby プログラム) を実行

    View full-size slide

  19. Ruby on Rails

    View full-size slide

  20. アンケート
    Rails について
    ● 使ったことがない #=> 13
    ● 業務経験やアプリケーションのリリース経験はないが使った
    ことはある #=> 2
    ● 業務経験やアプリケーションのリリース経験がある
    #=> 5

    View full-size slide

  21. Rails の特徴
    ● フルスタックフレームワーク
    ● MVC (Model-View-Controller) パターン
    ● Rails の基本理念
    ○ 同じことを繰り返すな (Don't Repeat Yourself: DRY)
    ○ 設定より規約 (Convention Over Configuration: CoC)

    View full-size slide

  22. Rails のメリット・デメリット
    ● Rails の用意した道 (The Rails Way) に乗ることで非常にす
    ばやく Web アプリケーションを開発できる
    ● The Rails Way で実現できないこともあり、そこから外れると
    大変なことが多い

    View full-size slide

  23. クックパッドにおける Rails
    ● レシピやマートをはじめ100以上のプロジェクトで利用
    ○ サービス開発を高速に進めるため
    ● バックエンドアプリケーションのスタンダート
    ○ とはいえ要件に応じて Go や Java (Spring Boot) なども
    採用している

    View full-size slide

  24. minimart API における Rails
    ● API のみでフロントエンドを持たず、最小限の機能しか利用
    しない
    ○ rails new minimart --api --minimal -d mysql
    ● GraphQL Ruby (後述) を利用するので、MVC の View や
    Controller もほとんど使わない
    ● Model に対応する Active Record の機能を主に利用

    View full-size slide

  25. Active Record
    ● オブジェクト/リレーショナルマッピング (ORM) を行う
    ● Active Record パターンが由来
    ○ DB のテーブルをクラス、レコードをそのインスタンスに対
    応させ、データアクセスのロジックをオブジェクトに持た
    せる
    ● データの操作を手軽に行える
    ● 一方で AR を継承したモデルが責務過多になりがち

    View full-size slide

  26. Active Record における「設定より規約」
    # データベースへの接続( Rails アプリでは config/database.yml の設定が利用される)
    ActiveRecord::Base.establish_connection(
    adapter: 'mysql2', host: 'localhost',
    username: 'root', password: '', database: 'minimart_development',
    )
    # User モデルに ActiveRecord::Base を継承させる
    class User < ActiveRecord::Base
    end
    # 規約により、クラス名から対応するテーブルが users になる
    User.table_name #=> "users"
    # 規約により、主キーは常に id
    User.primary_key #=> "id"

    View full-size slide

  27. CRUD: Create
    User.create(name: 'tomart')
    # INSERT INTO `users` (`name`, `created_at`, `updated_at`) VALUES
    ('tomart', '2021-08-18 10:00:00', '2021-08-18 10:00:00')
    user = User.new
    user.name = 'mini-tomart'
    user.save #=> true
    # INSERT INTO `users` (`name`, `created_at`, `updated_at`) VALUES
    ('mini-tomart', '2021-08-18 10:00:00', '2021-08-18 10:00:00')

    View full-size slide

  28. CRUD: Read
    User.all
    # SELECT `users`.* FROM `users`
    User.where('updated_at > ?', 1.day.ago).order(:updated_at)
    # SELECT `users`.* FROM `users` WHERE (updated_at > '2021-08-17 10:00:00')
    ORDER BY `users`.`updated_at` ASC
    User.find_by(name: 'tomart')
    # SELECT `users`.* FROM `users` WHERE `users`.`name` = 'tomart' LIMIT 1

    View full-size slide

  29. CRUD: Update
    user = User.find_by(name: 'tomart')
    user.update(name: 'mini-tomart') #=> true
    # UPDATE `users` SET `users`.`name` = 'mini-tomart', `users`.`updated_at` =
    '2021-08-18 10:00:00' WHERE `users`.`id` = 1
    user.name #=> "mini-tomart"
    user.name = 'tomart'
    user.save #=> true
    # UPDATE `users` SET `users`.`name` = 'tomart', `users`.`updated_at` =
    '2021-08-18 10:00:00' WHERE `users`.`id` = 1

    View full-size slide

  30. CRUD: Delete
    user = User.find_by(name: 'tomart')
    user.destroy #=> true
    # DELETE FROM `users` WHERE `users`.`id` = 1

    View full-size slide

  31. 関連付け (Association) の定義
    class User < ActiveRecord::Base
    # users テーブルは pickup_location_id という pickup_locations の外部キーを持ち
    # pickup_locations の主キーは id で対応するモデルは PickupLocation (規約)
    belongs_to :pickup_location
    end
    class PickupLocation < ActiveRecord::Base
    has_many :users
    end
    User PickupLocation
    n 1

    View full-size slide

  32. 関連付け (Association) の利用
    user = User.find_by(name: 'tomart')
    pickup_location = PickupLocation.create(name: 'WeWork みなとみらい')
    user.update(pickup_location_id: pickup_location.id)
    # User#pickup_location というメソッドが追加される
    user.pickup_location.name #=> "WeWork みなとみらい"
    # SELECT `pickup_locations`.* FROM `pickup_locations` WHERE
    `pickup_locations`.`id` = 1 LIMIT 1
    # PickupLocation#users というメソッドが追加される
    pickup_location.users.first.name #=> "tomart"
    # SELECT `users`.* FROM `users` WHERE `users`.`pickup_location_id` = 1

    View full-size slide

  33. DB スキーマ管理のための DSL (1/2)
    create_table :pickup_locations do |t| # 主キーとして id カラムを暗黙的に作成する
    t.string :name, null: false
    t.timestamps # created_at, updated_at というカラムを作成する
    end
    # CREATE TABLE `pickup_locations` (
    # `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
    # `name` varchar(255) NOT NULL,
    # `created_at` datetime NOT NULL,
    # `updated_at` datetime NOT NULL)

    View full-size slide

  34. DB スキーマ管理のための DSL (2/2)
    create_table :users do |t|
    t.belongs_to :pickup_location # pickup_location_id を作成してインデックスを貼る
    t.string :name, null: false
    t.timestamps
    t.index :name, unique: true # name にユニーク制約をつける
    end
    # CREATE TABLE `users` (
    # `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
    # `pickup_location_id` bigint,
    # `name` varchar(255) NOT NULL,
    # `created_at` datetime NOT NULL,
    # `updated_at` datetime NOT NULL)
    # CREATE INDEX `index_users_on_pickup_location_id` ON `users` (`pickup_location_id`)
    # CREATE UNIQUE INDEX `index_users_on_name` ON `users` (`name`)

    View full-size slide

  35. Ridgepole
    # DSL をもとに DB スキーマの変更を宣言的に行う gem
    # ridgepole コマンドを実行することで変更を適用できる
    # https://github.com/ridgepole/ridgepole
    create_table :pickup_locations do |t|
    t.string :name, null: false
    + t.string :address, null: false
    t.timestamps
    end
    # ALTER TABLE `pickup_locations` ADD `address` varchar(255) NOT NULL AFTER
    `name`

    View full-size slide

  36. GraphQL API Server

    View full-size slide

  37. アンケート
    GraphQL API サーバーについて
    ● 開発経験がない #=> 20
    ● Ruby 以外での開発経験がある #=> 0
    ● Ruby での開発経験がある #=> 0

    View full-size slide

  38. GraphQL Ruby
    ● GraphQL の Ruby 実装 (gem)
    ○ GraphQL のクエリ(文字列)を入力にデータ(Hash)を出
    力するまでのもろもろをいい感じにやってくれる
    ● サーバーの機能はもたない
    ○ Rails との連携がサポートされている
    ■ ハンズオンで実践

    View full-size slide

  39. # 入力 (GraphQL query)
    query {
    pickupLocations {
    name
    }
    }
    // 出力 (JSON)
    {
    "data": {
    "pickupLocations": [
    {
    "name": "WeWork みなとみらい"
    },
    {
    "name": "恵比寿ガーデンプレイスタワー"
    }
    ]
    }
    }
    GraphQL の入出力

    View full-size slide

  40. GraphQL Ruby を用いた場合
    class MinimartSchema < GraphQL::Schema # GraphQL Ruby より Graphql::Schema を継承
    # ここを起点に実装
    # ...
    end
    result = MinimartSchema.execute(<<~GRAPHQL) # ヒアドキュメント
    query {
    pickupLocations {
    name
    }
    }
    GRAPHQL
    result['data']['pickupLocations'][0]['name'] #=> "WeWork みなとみらい"
    puts result.to_json
    # {"data":{"pickupLocations":[{"name":"WeWork みなとみらい"},{"name":"恵比寿ガーデンプレイス
    タワー"}]}}

    View full-size slide

  41. 必要な実装
    ● スキーマの定義
    ○ GraphQL の型を Ruby のクラスで定義(コードファース
    ト)
    ● リゾルバ (resolver) の実装
    ○ 各フィールドに対し何を返すかを決めるメソッドを実装
    クエリのパースやバリデーション、結果の整形等は上記をもとに
    GraphQL Ruby がいい感じにやってくれる

    View full-size slide

  42. 以降の例で実現するスキーマ
    type Query {
    # すべての受け取り場所を返す
    pickupLocations: [PickupLocation!]!
    }
    # Active Record の説明で定義した PickupLocation モデルに対応
    # (データは DB の pickup_locations テーブルにある)
    type PickupLocation {
    id: ID!
    name: String!
    }

    View full-size slide

  43. GraphQL Ruby による型定義
    # GraphQL::Schema::Object を継承したクラスで GraphQL の型を定義する
    module Types
    class PickupLocationType < GraphQL::Schema::Object
    # filed クラスメソッドで定義する型のもつフィールドを定義する
    # 第一引数がフィールド名
    # 第二引数が返り値の型( Ruby の型も GraphQL の型に置き換えられる)
    field :id, ID, null: false
    field :name, String, null: false
    end
    end
    # クラス名から GraphQL の型名が決まる(規約)
    Types::PickupLocationType.graphql_name #=> "PickupLocation"

    View full-size slide

  44. module Types
    class QueryType < GraphQL::Schema::Object
    # 第一引数はスネークケースが慣習(キャメルケースに置き換えられる)
    # 第二引数に配列を渡すと GraphQL のリスト型と解釈される(直感的だが 不思議)
    field :pickup_locations, [Types::PickupLocationType], null: false
    end
    end
    class MinimartSchema < GraphQL::Schema
    # root となる Query 型に対応するクラスを指定
    query Types::QueryType
    end
    GraphQL Ruby によるスキーマ定義

    View full-size slide

  45. リゾルバの実装 (1/2)
    module Types
    class QueryType < GraphQL::Schema::Object
    field :pickup_locations, [Types::PickupLocationType], null: false
    # デフォルトでフィールド名と同名のメソッドがリゾルバになる
    # リゾルバでは返り値の GraphQL の型に対応する Ruby オブジェクトを返す
    # ここでは PickupLocation のインスタンスの Array(-like) を返している
    def pickup_locations
    PickupLocation.all
    end
    end
    end

    View full-size slide

  46. リゾルバの実装 (2/2)
    module Types
    class PickupLocationType < GraphQL::Schema::Object
    field :id, ID, null: false
    field :name, String, null: false
    # 自身の型に対応するアプリケーションのオブジェクトに object でアクセスできる
    def id
    object.id
    end
    # フィールド名のメソッドがない場合は object の同名メソッドを呼ぶので name は省略
    end
    end

    View full-size slide

  47. リゾルバ実装の注意点
    ● GraphQL の型とアプリケーションのオブジェクトとの対応関
    係は実装者が管理する
    ● 返り値の型が合わないとリゾルバで例外が発生したり
    GraphQL Ruby がエラーを返したりする
    ○ 例: non-null のフィールドで null を返した場合

    View full-size slide

  48. クエリの実行
    # ここまでのコードで以下が実現できる
    result = MinimartSchema.execute(<<~GRAPHQL)
    query {
    pickupLocations {
    name
    }
    }
    GRAPHQL
    result['data']['pickupLocations'].first['name'] #=> "WeWork みなとみらい"
    puts result.to_json
    # {"data":{"pickupLocations":[{"name":"WeWork みなとみらい"},{"name":"恵比寿
    ガーデンプレイスタワー "}]}}

    View full-size slide

  49. ハンズオン・課題で実践しながら確認
    ● Rails との連携
    ● Context
    ● Mutation
    ● 引数
    ● Input Objects
    ● Validation
    ● 認可 (Authorization)
    ● エラーハンドリング
    などなど適宜ドキュメントを参照しつつ

    View full-size slide

  50. アンケート
    gRPC について
    ● 聞いたことがない #=> 12
    ● 使ったことはないが聞いたことはある #=> 6
    ● 使ったことがある #=> 2

    View full-size slide

  51. Remote Procedure Call (RPC)
    ● ネットワーク上の別のマシンの手続きを呼び出す手法
    ○ 分散システムのための技術
    ● Web よりずっと歴史が長い
    ○ 遠隔手続き呼出し - Wikipedia によると1976年まで遡る

    View full-size slide

  52. gRPC の特徴
    ● HTTP/2 上で動作
    ○ HTTP は隠蔽されていて利用時は気にしなくてよい
    ● 様々な言語・プラットフォームで利用可能
    ● Protocol Buffers をデフォルトで使用
    ○ インターフェース定義言語 (IDL)
    ■ GraphQL のスキーマ定義 (schema.graphql) のようなもの
    ○ データのシリアライズ
    ■ GraphQL のデータのシリアライズフォーマットは基本的に JSON
    ● クライアントライブラリの自動生成

    View full-size slide

  53. gRPC Overview

    View full-size slide

  54. gRPC と GraphQL との比較
    ● 両者ともネットワークを介した API のための技術
    ● GraphQL が優れている点
    ○ クエリ言語による柔軟なデータ取得
    ● gRPC が優れている点
    ○ HTTP/2 と Protocol Buffers による効率的な通信
    ○ サーバーの実装が比較的シンプル

    View full-size slide

  55. クックパッドにおける gRPC
    ● マイクロサービス間の通信で利用
    ○ 詳しくは4日目のインフラ講義で
    ● 各サービスのインターフェース定義を単一
    のリポジトリで集中管理

    View full-size slide

  56. minimart API における gRPC
    ● gRPC API を叩いて注文完了時の決済を行う
    ● minifinancier が決済機能を提供
    ○ クックパッドの決済基盤 Financier が由来
    ■ https://techlife.cookpad.com/entry/2019/12/17/113612
    ○ Node.js & TypeScript 製
    ■ Ruby 以外の言語かつ Web フロントエンド講義で使用
    ○ 実際に決済を行うわけではなく実装は空
    ■ 本来は Stripe など決済代行の API を叩く

    View full-size slide

  57. gRPC の実装手順
    1. Protocol Buffers でインターフェースを定義
    2. 1 をもとに gRPC のコードを生成
    3. サーバー側の rpc を実装
    4. 生成されたコードでクライアントから rpc 呼び出し

    View full-size slide

  58. minifinancier の提供する RPC
    ● ユーザーへの請求を行う Charge という rpc を提供
    ● パラメータは以下の2つ
    ○ user_id: 請求対象のユーザー ID
    ○ amount: 請求金額(JPY)
    ● 返り値はPayment 型のメッセージ
    ○ 上記のパラメータに加えて請求 ID と請求時刻をフィール
    ドにもつ

    View full-size slide

  59. Protocol Buffers によるインターフェース定義
    // minifinancier.proto
    syntax = "proto3"; // v3 のシンタックスの使用
    package minifinancier; // 名前空間の分割( Ruby ではモジュールに対応)
    import "google/protobuf/timestamp.proto"; // 別ファイルやライブラリからメッセージ定義をインポート可能
    service PaymentGateway { // rpc をサービスという単位で定義
    rpc Charge(ChargeRequest) returns (Payment);
    }
    message ChargeRequest {
    uint64 user_id = 1; // フィールドごとにユニークな番号を割り当てる。バイナリエンコーディングで利用
    uint32 amount = 2;
    }
    message Payment {
    string id = 1;
    uint64 user_id = 2;
    uint32 amount = 3;
    google.protobuf.Timestamp create_time = 4; // ライブラリのものや独自のメッセージ型も使用可能
    }

    View full-size slide

  60. protoc によるコードの生成
    ● protocol buffer compiler (protoc) により .proto ファイル
    に定義したメッセージを扱うコードを生成できる
    ● gRPC では add-on を使ってサービス定義から rpc のコード
    も生成
    ○ Ruby 用ラッパー (gem) : grpc-tools
    ○ TypeScript 用ラッパー (npm): grpc_tools_node_protoc_ts
    ● (ハンズオンのリポジトリを使ってデモ)

    View full-size slide

  61. サーバーの実装 (Node.js & TypeScript)
    import { sendUnaryData, Server, ServerCredentials, ServerUnaryCall } from "@grpc/grpc-js";
    import { ChargeRequest, Payment } from "./minifinancier_pb"; // メッセージ定義から自動生成されたコード
    import { PaymentGatewayService } from "./minifinancier_grpc_pb"; // サービス定義から自動生成されたコード
    import { Timestamp } from "google-protobuf/google/protobuf/timestamp_pb";
    function charge(call: ServerUnaryCall, callback: sendUnaryData) {
    const response = new Payment()
    .setId("payment-42") // 説明のため決め打ち
    .setUserId(call.request.getUserId())
    .setAmount(call.request.getAmount())
    .setCreateTime(Timestamp.fromDate(new Date()));
    callback(null, response); // コールバックの第2引数に rpc の返り値を渡す(第
    1引数に値を渡すのはエラーの場合)
    }
    const server = new Server();
    server.addService(PaymentGatewayService, { charge }); // サービスと対応する rpc charge の実装をサーバーに追加
    server.bindAsync("0.0.0.0:50051", ServerCredentials.createInsecure(), () => {
    server.start(); // 50051 ポートで gRPC サーバーを起動
    });

    View full-size slide

  62. クライアントの実装 (Ruby)
    require 'minifinancier_services_pb' # 自動生成されたコードのロード
    # 自動生成されたコードから gRPC Stub を作成
    service = Minifinancier::PaymentGateway::Stub.new(
    'localhost:50051',
    :this_channel_is_insecure,
    )
    # gRPC Stub のメソッドを呼ぶと minifinancier の rpc が呼ばれる
    payment = service.charge(
    Minifinancier::ChargeRequest.new(user_id: 1, amount: 100),
    )
    payment.id #=> "payment-42"

    View full-size slide

  63. gRPC Overview (再掲)

    View full-size slide

  64. おわりに

    View full-size slide

  65. 公式ドキュメントを読もう
    ● Ruby
    ○ https://docs.ruby-lang.org/ja/3.0.0/doc/
    ○ https://rubyapi.org/
    ● Ruby on Rails
    ○ https://railsguides.jp/
    ○ https://api.rubyonrails.org/
    ● GraphQL Ruby
    ○ https://graphql-ruby.org/guides
    ● gRPC / Protocol Bufflers
    ○ https://grpc.io/docs/
    ○ https://developers.google.com/protocol-buffers/docs/proto3

    View full-size slide

  66. Happy Hacking!

    View full-size slide