GraphQLにおけるエラーハンドリングの仕方

f:id:vasilyjp:20190315124436p:plain

こんにちは、サーバーサイドエンジニアの竹若です。今回GraphQLにおけるエラーハンドリングを調査、Ruby on Railsとgraphql-rubyを使って実装する機会があったので、そこで得られた知見を共有させていただきたいと思います。(なお今回の実装はプロダクション環境には出ていません)

GraphQLの仕様とプラクティス

それではまず初めに、GraphQLが仕様に定めているレスポンスの返し方を見ていきましょう。

レスポンスのフォーマットに関するプラクティス

GraphQLのプラクティスの1つに、レスポンスのhttp statusを200で統一し、レスポンスのerrorsキーにエラーの詳細な情報を持たせるというものがあります。
なぜならGraphQLではリクエストに複数のクエリを含めることができるからです。

https://www.graph.cool/docs/faq/api-eep0ugh1wa/#how-does-error-handling-work-with-graphcool

Since GraphQL allows for multiple operations to be sent in the same request, it's well possible that a request only partially fails and returns actual data and errors.

これはあくまでプラクティスであり仕様ではないのですが、周辺ツール(Apolloやgraphql-ruby)がこのプラクティスに従っているため私たちも基本的には従うことになります。

レスポンスのフォーマットに関する仕様

ではGraphQLはどのようにしてエラーを表現するのでしょうか? GraphQLの仕様を見てみましょう。
https://facebook.github.io/graphql/June2018/#sec-Errors

GraphQLの仕様ではレスポンスのフォーマットはハッシュであり、中にdataerrorsというキーを含みます。

{
  "errors": [
    {
      "message": "hogehoge",
      "extensions": {
        "bar": "bar"
      }
    }
  ],
  "data": {
    "user": {
      "name": "takewaka"
    }
  }
}

data
クエリの実行結果が入るキーです。
クエリの実行前にエラーが発生した場合、dataキーはレスポンスに含まれません。

errors
クエリの実行中に発生したエラーが入るキーです。
クエリの実行中にエラーが発生しなかった場合、errorsキーはレスポンスに含まれません。
またレスポンスにdataキーが含まれない場合、errorsキーは必ずレスポンスに含まれている必要があります。

なおerrorsのフォーマットも仕様で定められていて、中にmessage,location,pathというキーが含まれます。(locationpathはクエリの中にエラーの該当箇所が存在する場合にのみ含まれます)
上記3つのキーの他にキーを追加したい場合は、extensionsというキーを用意してその中に追加する仕様です。
なぜならmessagepathなどのキーと同じレベルにオリジナルのキーを追加してしまうと、そのオリジナルのキーと将来的に仕様に追加されるキーがバッティングを起こす可能性があるからです。

https://facebook.github.io/graphql/June2018/#sec-Errors

GraphQL services should not provide any additional entries to the error format since they could conflict with additional entries that may be added in future versions of this specification.

GraphQLを使っていて発生するエラーとその分類

さて、GraphQLがどのようにしてエラーを表現するかはわかりました。
次にGraphQLを使っていて起こるエラーにはどのようなものがあるのか見ていきましょう。

エラーを以下の2つの観点で見ていきます。

  • エラーの原因はクライアントなのか、サーバーサイドなのか
  • エラーはどこで発生したか

クライアントが原因のエラーは主に以下の3つに分類できます。

  • パースエラー
    • クエリのシンタックスエラー
  • バリデーションエラー
    • クエリが型チェックで引っかかる
  • クエリ実行時エラー
    • 認証失敗など

サーバーサイドが原因のエラーはRailsで実装したロジックのエラーです。

どのようなエラーが存在するかわかったところで、それぞれのエラーをどのような形のレスポンスで表現するのか見てみましょう。

Apolloなどのクライアントがレスポンスをパースしやすいように、レスポンスのフォーマットはGraphQLの仕様に準拠した形で統一したいです。
そこでerrorsキーの中のmessageキーにエラーの詳細なメッセージを入れて、extensionsキーの中のcodeキーにステータスコードを入れる方法をここでは見ていきます。
これはGraphQLの仕様書に載っているエラーレスポンスの例と同じ方法です。
またApollo Serverでは、extensionsキーの中にcodeをはじめとしたエラーに関するキーを含める方法を採用しています。
例えば認証エラーであればこのようにcodeの中にAUTHENTICATION_ERRORというステータスを入れます。

"errors": [
    {
      "message": "permission denied",
      "locations": [],
      "extensions": {
        "code": "AUTHENTICATION_ERROR"
      }
    }
  ]

サーバーエラーの場合はこのようにcodeの中にINTERNAL_SERVER_ERRORというステータスを入れます。

"errors": [
    {
      "message": "undefined method 'hoge' for nil",
      "locations": [],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR"
      }
    }
  ]

graphql-rubyで実装する方法

エラーをどのような形のレスポンスで表現するか決まったところで、graphql-rubyで実際に実装していきましょう。

graphql-rubyではエラーをどう拾ってどう返すか

graphql-rubyではGraphQL::ExecutionErrorかもしくはそのサブクラスをraiseすることでerrorsにエラーを含めることができます。

def resolve(name:)
  user = User.new(name: name)
  if user.save
    { user: user }
  else
    raise GraphQL::ExecutionError, user.errors.full_messages.join(", ")
  end
end

認証エラー

例として認証エラーの実装を載せます。
この例ではログインをセッションで管理していてcurrent_userメソッドを呼ぶことでユーザーオブジェクトが取得できる設定です。
コントローラーでGraphQL::Schema#executeを実行する際にユーザーのログイン情報をcontextに入れて引数として持たせておきます。

class GraphqlController < ApplicationController
  def execute
    variables = ensure_hash(params[:variables])
    query = params[:query]
    operation_name = params[:operationName]
    context = { current_user: current_user }
    result = SampleSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
    render json: result
    #...
  end
  #...
end

resolveメソッドの中でcontextに入っているユーザーのログイン情報を見て認証エラーを吐かせています。
GraphQL::ExecutionErrorはキーワード引数としてextensionsを持っているのでオリジナルのキー(ここでいうcode)を渡すことができます。

def resolve(name:, sex:)
  raise GraphQL::ExecutionError.new('permission denied', extensions: { code: 'AUTHENTICATION_ERROR' }) unless context[:current_user]

  #...
end

そうすることで認証エラーが発生した場合、このようなフォーマットでレスポンスを返すことができます。

"errors": [
    {
      "message": "permission denied",
      "locations": [
        {
          "line": 3,
          "column": 3
        }
      ],
      "path": [
        "createUser"
      ],
      "extensions": {
        "code": "AUTHENTICATION_ERROR"
      }
    }
  ]

セーフティネット

GraphQLのレスポンスはクライアントがパースしやすいようにフォーマットを統一することが重要です。
発生したエラーが最後までrescueされずにいるとRailsの一般的な500エラーが返ってしまいます。
そうなるとクライアント側はhttp status 200で返ってくるGraphQLのエラーと、Railsの一般的な500エラーの両方をパースする準備をしなければなりません。
そこでサーバーサイドでエラーを最終的に受け止めるセーフティネットを用意したくなります。
graphql-ruby 1.8まではrescue_fromメソッドを使ってこれを実現できます。
以下にrescue_fromメソッドを使った実装例を示します。

class SampleSchema < GraphQL::Schema
  rescue_from(StandardError) { 'INTERNAL_SERVER_ERROR' }

#...
end

こうすることでRailsの一般的な500エラーではなく、以下のようなGraphQLのエラーを返すことができます。

"errors": [
    {
      "message": "INTERNAL_SERVER_ERROR",
    }
  ]

ただrescue_fromの欠点として、errors内のmessageキーの内容しか指定できないという点があります。
これはgraphql-errorsというgemを使ってrescue_fromにエラークラスのオブジェクトを渡すことで解決します。
GitHub - exAspArk/graphql-errors: Simple error handler for GraphQL Ruby

しかしgraphql-rubyの機能としてrescue_fromにエラーオブジェクトを渡せてもいいのではないかと考えたのでパッチを書きました。
extend GraphQL::Schema::RescueMiddleware#attempt_rescue by masakazutakewaka · Pull Request #2140 · rmosolgo/graphql-ruby · GitHub

このパッチは以下のようにブロックにGraphQL::ExecutionErrorオブジェクトを渡せるようにすることでextensionsキーを使えるようにするものです。

class SampleSchema < GraphQL::Schema
  rescue_from(StandardError) do |message|
    GraphQL::ExecutionError.new(message, extensions: {code: 'INTERNAL_SERVER_ERROR'})
  end

#...
end

またこのrescue_fromメソッドはgraphql-ruby 1.9から使えなくなります。
理由はrescue_fromメソッドが定義されているGraphQL::Schema::RescueMiddlewareクラスがgraphql-ruby 1.9から使えなくなるからです。
GraphQL - Interpreter

graphql-ruby 1.9では現状rescue_fromメソッドに変わる何かは存在せず、どのような実装が追加されるかも未定というステータスです。
GraphQL::Execution::Interpreter and rescue_from compatibility · Issue #2139 · rmosolgo/graphql-ruby · GitHub

個人的にはgraphql-errorsがgraphql-rubyに上手く取り込まれてくれたらいいなと思っています。

複数エラー

クエリを実行して発生した複数のエラーを1つのレスポンスに含めたい場合があります。
例えばユーザー登録などの複数の入力項目を持つMutationがあったとします。
入力内容が不正であった全ての入力項目にエラーメッセージを表示したい場合、複数のエラーをレスポンスに含めたくなります。
graphql-rubyにおいてはGraphQL::Schema::Context#add_errorを使うことで複数エラーをレスポンスに含めることができます。
https://www.rubydoc.info/github/rmosolgo/graphql-ruby/GraphQL%2FQuery%2FContext%2FFieldResolutionContext:add_error

以下は実装例です。

module Mutations
  class CreateUser < GraphQL::Schema::RelayClassicMutation
    argument :name, String, required: true
    argument :sex, String, required: true

    field :user, Types::UserType, null: true

    def resolve(name:, sex:)
      user = User.new({ name: name, sex: sex })
      if user.save
        { user: user }
      else
        build_errors(user)
        return # これがないとrescue_fromに拾われてしまう
      end
    end

    def build_errors(user)
      user.errors.map do |attr, message|
        message = user[attr] + ' ' + message
        context.add_error(GraphQL::ExecutionError.new(message, extensions: { code: 'USER_INPUT_ERROR', attribute: attr }))
      end
    end
  end
end

複数のエラーを含んだレスポンスは以下のようになります。

"errors": [
    {
      "message": "hoge はすでに存在します",
      "extensions": {
        "code": "USER_INPUT_ERROR",
        "attribute": "name"
      }
    },
    {
      "message": "fuge は一覧にありません",
      "extensions": {
        "code": "USER_INPUT_ERROR",
        "attribute": "sex"
      }
    }
  ]

また独自のエラータイプを定義してエラーの内容をdataに含めるという方法も存在します。
https://github.com/rmosolgo/graphql-ruby/blob/master/guides/mutations/mutation_errors.md#errors-as-data

しかしGraphQL::Schema::Context#add_errorを使う方法を以下の理由で採用しました。

  • GraphQLの仕様上errorsdataと同じレベルに位置してる
  • dataの中にエラーを入れる場合、クエリ内にエラーのフィールドを明示的に書かないとエラーの情報を得ることができないのでエラーの受け渡し方として優れていない

独自のエラータイプを定義してエラーの内容をdataに含める方法にも以下のような利点があると思います。

  • エラータイプを定義するのでエラーの構造をスキーマで共有できる
    • クライアント側でエラーメッセージを表示したい場合に、エラーの情報をレスポンスから取り出すのが楽

まとめ

この記事ではGraphQLにおけるエラーハンドリングの仕方とgraphql-rubyを使った実装例を紹介しました。
この記事の初めにGraghQLの仕様に軽く触れましたが、GraphQLの仕様はとても簡潔にまとめられているので読むことをお勧めします。
また紹介したgraphql-ruby には見やすい場所にドキュメントされていない隠れAPIがあったりするので、ソースコードやissueを読んでみると色々発見できると思います。
この記事の最後の方で紹介したadd_errorメソッドが隠れAPIの1つです。
将来的にはプロダクションに出してから得られる知見も発信したいです。
GraphQLを使った開発に興味のある方がいましたら、ぜひ以下のリンクからご応募ください。お待ちしております!

www.wantedly.com

参考

RubyConf2018参加レポート

f:id:vasilyjp:20181117070815j:plain

こんにちは、サーバーサイドエンジニアの竹若です。11/13 ~ 11/15にかけてロサンゼルスで開催されたRubyConf2018にZOZOテクノロジーズから竹若・高木(@rllllho)・田島(@katsuyan121)の3人が参加しました。

今年のRubyConfは講演数60、参加者数840の大規模なカンファレンスでした。この記事では私たちが興味を持った講演をいくつか紹介させていただきます。

  • Opening Keynote
  • Sweat the Small Stuff
  • Being Good: An Introduction to Robo- and Machine Ethics
  • Empowering Early-Career Developers
  • Ethical Data Collection for Regular Developers
  • The Ruby Developer's Command Line Toolkit
  • Pointers for Eliminating Heaps of Memory
  • Parallel programming in Ruby 3 with Guild
  • Building Serverless Ruby Bots
  • Runnning a Government Department on Ruby for over 13 Years
  • It's Down! Simulating Incidents in Production
  • Yes, You Should Provide a Client Library For Your API
  • Unlearning: The Challenge of Change
  • まとめ
続きを読む

OpenAPIのschema定義からRubyのクラスを生成するgem「openapi2ruby」をつくりました

f:id:vasilyjp:20180927112712j:plain

こんにちは。スタートトゥデイテクノロジーズ新事業創造部のid:takanamitoです。
今日はVASILY時代から活用されているOpenAPI(Swagger)の定義からRubyのクラスを自動生成するgemを作ったので、その紹介をしようと思います。

Swaggerの定義と実際のAPIが返すレスポンスの内容がズレている

弊社ではVASILY時代からSwaggerの導入が進んでいましたが、徐々に「Swaggerの定義と実際のAPIが返すレスポンスの内容がズレている」といった問題が発生しはじめていました。

その問題を解決するために今回つくったのがこのgemです。 github.com

続きを読む

RubyKaigi2018参加レポート

f:id:vasilyjp:20180927112707j:plain

こんにちは、バックエンドエンジニアの田島(@katsuyan121)です。 5/31〜6/2にかけて仙台で開催されたRubyKaigi2018に、スタートトゥデイテクノロジーズから5人が参加しました。

今年のRubyKaigiは3日間で50を超える講演があり、参加者も1000人を超える大変大規模なカンファレンスでした。たくさんの講演の中で、スタートトゥデイテクノロジーズのエンジニアが興味を持ったものを、この記事でいくつか紹介します。 また今回スタートトゥデイテクノロジーズはスポンサーブースを出展したので、そこで得られたことを共有します。

続きを読む

RubyのパパMatzさんに、RubyKaigi2017の見所を聞いてみた

f:id:vasilyjp:20180927110202j:plain

こんにちは、VASILYバックエンドエンジニアの塩崎です。 RubyKaigi2017の開催時期が間近に迫っていますが、皆さんの広島グルメ探訪の予定はいかがでしょうか?

今年のRubyKaigiにはVASILYから4人が参加する予定で、そのうちの3人は初参加です。 発表の要旨はすでに公開されていて以下のページで確認できますが、まだどれを見て回ろうかを決めかねている人もいるかと思います。 http://rubykaigi.org/2017/schedule

そこで、Rubyのパパであり、VASILYの技術顧問でもあるまつもとゆきひろさん(以下、Matzさん)にRubyKaigi2017の見所を聞いてみました。 この記事がRubyKaigiに参加をされる方々の参考になれば幸いです。

続きを読む

【Rails】WEB APIを長く運用するための仕組み化

f:id:vasilyjp:20170328122358j:plain

こんにちは、バックエンドエンジニアのじょーです。大規模なサービスのAPIを開発する際に、ルールを決めずに開発していると無秩序なコードが散見される運用がしづらいAPIになってしまいます。また、ルールを決めたとしても共有が上手くいかないなどの理由で守られなくなってしまうこともあると思います。 本記事では、APIを運用しやすくするために、ただルールを決定しただけではなく、ルールを守るためにそれぞれ仕組み化をしたことを紹介します。

続きを読む

horensoで作るモダンなcronスクリプト監視環境

f:id:vasilyjp:20180927090637j:plain

こんにちは。
モルトとシガーで生きてます。インフラエンジニアの光野(@kotatsu360)です。

先日、crontabで管理しているバッチ処理の監視にhorensoというツールを導入したのですが、

  • 監視の品質が向上
  • 毎分届く大量の実行結果メールから開放されQoL向上

という効果がありました。本日はその取り組みについてご紹介いたします。

続きを読む

RailsアプリケーションにおけるModelキャッシュの実装

f:id:vasilyjp:20180927090637j:plain

こんにちは、バックエンドエンジニアのjoeです。主にAPIを担当しています。 VASILYのAPIでは、速度向上のためにModelオブジェクトをキャッシュしています。 最近、Modelキャッシュの仕組みを実装したので、その実装方法を紹介します。また、既存ライブラリとの比較についても書きたいと思います。

Modelキャッシュとは

Modelキャッシュを簡単に言うと、下記の結果をキャッシュすることです。

> Item.find(1)
=> #<Item:0x007fdfe398a678>

このように、1レコード単位のActiveRecordをキャッシュすることを本記事ではModelキャッシュと呼びます。ActiveRecordをキャッシュすることで、データベースへの読み込み回数を減らし、レスポンス速度を向上させることができます。

続きを読む

WebアプリのAPIリクエスト効率化

f:id:vasilyjp:20160808163934j:plain

Webフロントエンドエンジニアの権守です。 今回は、iQONのWebアプリのAPIリクエスト部分の仕組みを改善したことについて紹介します。

前提

このブログでも何度か紹介していますが、iQONでは、ネイティブアプリとWebアプリの両方で、共通のAPIを利用して開発を行っています。 そのため、通常のRailsアプリケーションと異なり、iQONのWebアプリ版のモデル部分では、DBへのアクセスを行わずAPIへのアクセスを行い、データを取得します。 こういった形式を扱うGemとしてはherなどがありますが、iQONでは、完全にREST形式でない、並列でリクエストを行いたいなどの理由から自前で実装しています。

続きを読む

iOSの月額課金レシート検証をサーバーサイドで行うときのTipsまとめ

f:id:vasilyjp:20180927090637j:plain

こんにちは、バックエンドエンジニアのjoeです。
みなさんはお気に入りのアプリに月額課金をしたことがありますか?したことがない人は今すぐお気に入りのアプリをみつけて月額課金しましょう!
実際にiOSで月額課金をすると、課金の証明としてAppStoreがレシートを発行します。レシートと言ってもAppStoreが紙のレシートを送りつけてくるわけではなく、電子的な購入情報のことをレシートと呼びます。ユーザーが解約処理をしない限りAppStore側でレシートが自動更新される仕組みになっています。(月額課金の場合)
その際に、AppStoreのサーバーにHTTPのPOSTリクエストでレシートを問い合わせ、現在の課金状況を知ることができます。このお問い合わせ処理と、レシートが不正なレシートでないかをチェックする処理を合わせてレシート検証と呼びます。
今回はiOSのレシート検証をクライアントのみでの検証からサーバーサイドでの検証に実装を変更したので、その理由や方法、考慮するべき点などを書きます。

注意

この情報は2016年8月3日現在のものです。

サーバーサイドでのレシート検証が推奨される理由

続きを読む

アドサーバーをElasticBeanstalk with Dockerに移行した話

f:id:vasilyjp:20180927090637j:plain


こんにちは、神崎です。今年の6月頃にアドサーバーのアプリケーションサーバ群をAWS ElasticBeanstalk w/ Dockerに置き換えをおこないました。
これにより、アプリケーションレイヤーのauto-scaling環境の構築、deployのフローの自動化、rubyなどのMiddlewareの入れ替えが比較的容易にできる環境になりました。
既存の環境は、以前ブログで紹介したとおり、EC2上にruby2.0でunicorn+sinatraで構築していました。

iQONの広告配信システム - VASILY DEVELOPERS BLOG

 

続きを読む

最強のプッシュ通知には必要不可欠!グロースハックの為のAPNs Feedbackサービス導入方法

f:id:vasilyjp:20180927090637j:plain


バックエンドエンジニアのBoBです。 今回はiOSプッシュを最適化するためには必要不可欠な、Apple Push Notification service(以下APNs)のFeedbackサービスについて紹介したいと思います。 Push

はじめに

iPhoneやAndroidにはプッシュ通知という仕組みがあることは皆さんもよくご存知かと思いますが、より洗練された、効果的なプッシュ通知を送るために自分たちのプロダクトが送っているプッシュ通知を分析する事はとても重要な事です。 今回はiPhoneのFeedbackサービスを利用して、プッシュ通知を送れていないユーザーを把握する事に関して 1. プッシュ通知の意義 2. Feedbackサービスの説明 という2点から紹介しようと思います。

プッシュ通知の意義

iPhoneやAndroidのプッシュ通知は、ユーザーのRetention(再訪率)を高める効果があります。 弊社グロースハックブログの 広告費0円でもDAU10倍!? ユーザーRetention(再訪率)を最大化する施策10選 の記事にも、Retentionを上げる意義やそのためのプッシュ通知の必要性などが書いてあるのでぜひ参考にしてください。 しかし、ただ闇雲にプッシュ通知をユーザーに送っていてもプロダクトのRetentionを高める事にはつながりません。 プッシュ通知を最適化するためには、実際にプッシュ通知の様々な部分を日々工夫し、その結果どのようなプッシュ通知がユーザーのRetentionを高める効果があるかを分析しなければいけません。 その分析を行うためには様々な数値を正確に知る必要があります。 プッシュ通知改善の為に日々変更を加える部分は大きく分けて以下の2点があるかと思います。 ・タイミング(プッシュ通知を送る時間) ・内容/見た目 これらを変更した際の効果を測定するためには以下のような数値を知る必要があります。 ・ 何人のユーザー(何個の端末)を対象としたプッシュ通知なのか ・ 実際には何人のユーザーに送信されているのか ・ 何人のユーザーがプッシュ通知からアプリを起動したか APNsのFeedbackサービスでは、「実際にユーザーに届ける事ができなかったプッシュ通知の情報」を取得することができます。 つまり、上記の例で言うと二つ目にある「実際に何人のユーザーに送信されているか」というデータを知るために必要不可欠な仕組みとなっています。

Feedbackサービスとは?

APNsにはFeedbackサービスという、正常に処理できなかったプッシュ通知に関する情報を返す仕組みがあります。 Feedbackサービスに接続したときに得られる情報は、前回Feedbackサービスの情報を取得した以降、配送に失敗した通知に関するものだけで、Feedbackサービスが保持しているリストは読み取り後クリアされます。

Feedbackサービスの導入方法

Feedbackサービスはプッシュ通知の仕組みを実装していれば非常に簡単に導入でき、使い方次第でサービスのグロースに絶大な効果を上げる事ができます。

1. 各種設定

Feedbackサービスを利用する際は以下のサーバー、ポートにSSL接続します。 key, certの設定はプッシュ通知を送る際と同じpemファイルを設定します。 [ruby] require 'socket' require 'openssl' # 開発環境 apns-dev-key-noenc.pem, apns-dev.pem # 本番環境 apns-prd-key-noenc.pem, pns-prd.pem context = OpenSSL::SSL::SSLContext.new('SSLv3') context.key = OpenSSL::PKey::RSA.new(File.read("apns-prd-key-noenc.pem")) # プッシュ通知を送る処理と同じpemファイル context.cert = OpenSSL::X509::Certificate.new(File.read("pns-prd.pem")) # プッシュ通知を送る処理と同じpemファイル [/ruby]

2. 結果取得処理

ssl通信で結果を取得します。 [ruby] # 開発環境: feedback.sandbox.push.apple.com # 本番環境: feedback.push.apple.com sock = TCPSocket.new('feedback.sandbox.push.apple.com', 2196) # Feedbackサービスのポートは2196で固定 ssl = OpenSSL::SSL::SSLSocket.new(sock, context) ssl.sync = true ssl.connect while line = ssl.read(38) # SSLソケットから38バイト取得する  # feedbackで取得したデータを用いた処理をここで実装する p line.unpack('N1n1H140') end ssl.close sock.close [/ruby]

3. レスポンス

上記の結果は以下のような形で出力されます。 [ruby] [1392300474, 32, "#プッシュ対象のdevice id"] [1392303094, 32, "#プッシュ対象のdevice id"] [1392295196, 32, "#プッシュ対象のdevice id"] [1392301807, 32, "#プッシュ対象のdevice id"] ........ [/ruby] Feedbackサービスから取得できる情報で利用できるものはプッシュ通知対象のdevice idのみですが、この各レスポンスからどのユーザーのプッシュ通知かを判断し、分析をかける事ができま す。

まとめ

今回APNsのFeedbackサービスによる送信失敗結果の取得方法を紹介しました。しかしここで説明した内容を実装しただけではサービスのグロースには何もつながりません。 ここで取得したデータをいかにうまく使って分析していくかが重要であり、今回紹介した内容はプッシュ通知最適化の入り口でしかないのです。 現にVASILYではFeedbackサービスで取得したデータ以外にも様々なログを用いてユーザーに送るプッシュ通知を分析し、より効果的なプッシュ通知を目指して日々改善しています。 最後になりますが、VASILYでは現在エンジニアを大募集しています! 直近ですとiQONを支えているアグレッシブな技術がどのようなものか紹介する勉強会や、会社での飲み会が予定されています。 【第2回】iQONエンジニアセミナー ~進化し続けるiQONを支えるグロースハック的エンジニアリング手法とは?~ 【2/21】「iQON」急成長を支えるエンジニアと話したい人、WANTED VASILY/iQONに少しでも興味がある方はぜひご参加ください!

カテゴリー