31
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GraphQLAdvent Calendar 2019

Day 7

AppSyncで自分のデータだけSubscription

Last updated at Posted at 2019-12-06

はじめに

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
31
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?