Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
22
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

AppSyncで自分のデータだけSubscription

はじめに

AppSyncのサンプルアプリとかはあちこちありますが、自分のデータだけ Subscriptionする方法が、最初取り組んだときに分かりにくかったので、ここにまとめます。

サンプル

チャットアプリを考えます。
ここでは簡単のために、1対1のチャットを考えます。
以下は、まだSubscriptionを定義していないサンプルスキーマです。

schema.graphql
type ChatRoom {
  id: ID!
  userIds: [Int!]!
}

type Message {
  chatRoomId: ID!
  fromUserId: Int!
  sendTimestamp: AWSTimestamp!
  content: AWSJSON!
}

type Mutation {
  addMessage(chatRoomId: ID!, toUserId: Int!, content: AWSJSON!): Message!
}

type Query {
  # 本当はページネーションとか考慮いるけど省略
  chatRooms: [ChatRoom!]!
  messages(chatRoomId: ID!):[Message!]!
}

schema {
  query: Query
  mutation: Mutation
}

Subscriptionの設定

チャット相手がメッセージを送信したことを検知できるように、addMessageをSubscriptionします。

とりあえずSubscription定義

type Subscription {
  onAddMessage: Message @aws_subscribe(mutations: ["addMessage"])
}

とりあえずsubscriptionを定義しました!!
ただこの状態だと、自分のデータだけでなく他人のデータを含む全てのメッセージをSubscriptionします😆😆😆

Subscriptionの引数を定義

自分のデータだけSubscriptionするようにしましょう。以下はスキーマの関連する部分を抜き出しています。

type Message {
  chatRoomId: ID!
  toUserId: Int! # 追加!!
  fromUserId: Int!
  sendTimestamp: AWSTimestamp!
  content: AWSJSON!
}

type Mutation {
  addMessage(chatRoomId: ID!, toUserId: Int!, content: AWSJSON!): Message!
}

type Subscription {
  onAddMessage(toUserId: Int!): Message @aws_subscribe(mutations: ["addMessage"])
}

AppSyncでは、Subscriptionに引数を追加することで、特定のデータだけSubscriptionする仕組みがあります。
https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/real-time-data.html#using-subscription-arguments

ここでは「自分に送信された」メッセージだけSubscriptionしたいので、引数にtoUserIdを追加します。
さらに、toUserIdで絞り込むためには、MutationのaddMessageの戻り値にtoUserIdを含む必要があります。
(1対1のチャットを前提としているので、MessageはfromUserIdだけあれば問題ないのですが、SubscriptionのためにtoUserIdを冗長に持たせています。改善の余地はありますが、とりあえず説明用のサンプルとして)

これで送信先が一致する場合のみSubscriptionできるようになりました!!

が、まだダメです、、toUserIdに自分のIDではなく他人のIDを指定した場合、他人のメッセージをSubscriptuonできちゃいます😆😆😆

AppSync側でtoUserIdを確認する

ここでは、AppSyncのAPIはCognito認証が必要であり、Cogniteユーザの属性に自分のuserIdを含む前提となります。

まずは、Subscription(onAddMessage)にリゾルバーをアタッチします。

Screen Shot 2019-12-06 at 23.44.41.png

リゾルバーのリクエストマッピングテンプレートは何もする必要はありません。

レスポンスマッピングテンプレートで、
Subscriptionの引数で指定されたtoUserIdと、認証トークンに含まれるuserIdが一致するか確認し、不一致の場合は認可エラーを返します。

Screen Shot 2019-12-06 at 23.45.17.png

toUserIdに自分以外のidを設定した場合は、エラーを返すようになりました。
これで完成です!!

参考:https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/security-authorization-use-cases.html#real-time-data

まとめ

Subscriptionを含めたスキーマ全体です。

schema.graphql
type ChatRoom {
  id: ID!
  userIds: [Int!]!
}

type Message {
  chatRoomId: ID!
  toUserId: Int!
  fromUserId: Int!
  sendTimestamp: AWSTimestamp!
  content: AWSJSON!
}

type Mutation {
  addMessage(chatRoomId: ID!, toUserId: Int!, content: AWSJSON!): Message!
}

type Query {
  chatRooms: [ChatRoom!]!
  messages(chatRoomId: ID!):[Message!]!
}

type Subscription {
  onAddMessage(toUserId: Int!): Message @aws_subscribe(mutations: ["addMessage"])
}

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

↓レスポンスマッピングテンプレートのコピペ用

#if($context.identity.claims.get("custom:user_id") != \${context.arguments.toUserId})
  $utils.unauthorized()
#else
  null
#end
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
22
Help us understand the problem. What are the problem?