Amplify × AppSyncでSubscriptionをフィルタリングする

## 概要

リアルタイムデータの反映のためにsubscriptionを利用しています。@modelでsubscriptionを自動生成していましたが、自動生成ではフィルタリングができず、関係のないデータが一時的に表示されてしまう問題がありました。そこで、subscriptionを手動で作成し、引数でフィルタリングするようにしました。

## 実装

### スキーマ

これまでは@modelでsubscriptionを自動生成していました。

```
type Task
@model(subscriptions: { level: public })
{
  id: ID!
  name: String!
  detail: String
}
```
↓生成

```
export const onCreateTask = /* GraphQL */ `
  subscription OnCreateTask {
    onCreateTask {
      id
      name
      detail
    }
  }
`;

export const onUpdateTask = /* GraphQL */ `
  subscription OnUpdateTask {
    onUpdateTask {
      id
      sequence
      name
      detail
    }
  }
`;

export const onDeleteTask = /* GraphQL */ `
  subscription OnDeleteTask {
    onDeleteTask {
      id
      name
      detail
    }
  }
`;
```

しかし、自動生成ではフィルタリングできません。
そのため、@modelでsubscriptionを自動生成しないように設定します。

```
type Task
@model(subscriptions: null)
{
  id: ID!
  name: String!
  detail: String
}
```

次に独自のsubscriptionを```schema.graphql```に追加します。
```Subscription名(引数): 型 @aws_subscribe(mutations: [Mutation名])```のように書きます。

```
type Subscription {
  onCreateTaskByName(name: String): Task
    @aws_subscribe(mutations: ["createTask"])

  // Mutationのtypeはcreate・update・deleteの3種類。必要に応じて設定。
  onUpdateTaskByName(name: String): Task
    @aws_subscribe(mutations: ["updateTask"])
  onDeleteTaskByName(name: String): Task
    @aws_subscribe(mutations: ["deleteTask"])
}
```

↓生成

```
export const onCreateTaskByName = /* GraphQL */ `
  subscription OnCreateTaskByName($name: String) {
    onCreateTaskByName(name: $name) {
      id
      name
      detail
    }
  }
`;

export const onUpdateTaskByName = /* GraphQL */ `
  subscription OnUpdateTaskByName($name: String) {
    onUpdateTaskByName(name: $name) {
      id
      name
      detail
    }
  }
;`

export const onDeleteTaskByName = /* GraphQL */ `
  subscription OnDeleteTaskByName($name: String) {
    onDeleteTaskByName(name: $name) {
      id
      name
      detail
    }
  }
;'
```

これでsubscriptionに引数が渡せるようになり、フィルタリングをすることができるようになりました。

また、引数を複数設定することも可能です。引数を複数設定した場合はその引数に完全一致した場合のみsubscribeします。

```
type Subscription {
  onCreateTaskByName(name: String, detail: String): Task
    @aws_subscribe(mutations: ["createTask"])
}
```

## 呼び出し

```
import Amplify, { API } from "aws-amplify";
import { subscriptions } from "src/graphql/subscriptions";

function subscribe() {
    const subscription = API.graphql({
      query: subscriptions.onCreateTaskByName,
      variables: {
        name: "Test",
      },
    }).subscribe({
      next: ({ provider, value }) => {
        console.log("Subscribe:", { provider, value });
        subscription.unsubscribe();
      },
      error: (error) => console.warn(error),
    });
};
```

これで「Test」というnameのタスクを作成した時だけsubscribeできるようになりました。

graphqlOperationを利用する場合は下記のようにします。

```
import Amplify, { API, graphqlOperation } from "aws-amplify";
import { subscriptions } from "src/graphql/subscriptions";

function subscribe() {
    const subscription = API.graphql(
      // TypeScriptを利用している場合は型定義も可能
      graphqlOperation(subscriptions.onCreateTaskByName as 型, {
        name: "Test",
      })
    ).subscribe({
      next: ({ provider, value }) => {
        console.log("Subscribe:", { provider, value });
        subscription.unsubscribe();
      },
      error: (error) => console.warn(error),
    });
};
```

## 注意点

- mockで動作しない

これでハマりました...
issueにmockで動作しないことが挙がっていました。closeされていますが、改修はされていないようです。そのため、毎回codgenを実行し、pushする必要があります。また、codegenでsubscriptionが自動生成されないことがありました。その場合は一度mockを起動すると生成可能です。
＊2021/03 現在

issue：https://github.com/aws-amplify/amplify-cli/issues/6026

## 参考
(公式)
- [Examples](https://docs.amplify.aws/cli/graphql-transformer/examples)
- [リアルタイムデータ](https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/real-time-data.html)
- [Subscribe to data](https://docs.amplify.aws/lib/graphqlapi/subscribe-data/q/platform/js)
(ブログ)
- [AWS AppSync + Amplifyで、Subscriptionに引数を追加し、一部のみ通知を受け取ってみた](https://thinkami.hatenablog.com/entry/2019/07/22/062759)

<br>
- Miyata Koki -
[O:inc.](https://o-inc.jp/)でAmplify×React×React Nativeを使用して開発しています。大学のゼミでは統計学をPythonで行っています。
インターンやゼミで学んだ情報を発信していくので、フォロバするのでぜひこちらのアカウントのフォローお願いします！