[AWS Amplify API] createdAtでソートした結果を返す

2021-12-11AWS Amplify,IT記事

追記(2021/12/11)—–

Amplifyの更新によりGraphQL Transformerはv2になり記述が大きく変わりました。以下のリンクを参考にしてください。

https://docs.amplify.aws/guides/api-graphql/query-with-sorting/q/platform/js/#overview

この記事も前のバージョンのやり方として残しておきます。

—–

AWS AmplifyのGraphQLで1つ目のテーブルを作るとする。例えばTodo型なら以下のようになる。

type Todo
  @model
 {
  id: ID!
  title: String!
  createdAt: AWSDateTime
}

ここから

・list クエリで取得したとき、"createdAt"でソートされていてほしい。

・get で idを指定した取得もできるようにしたい。

その場合下記のようにする。

type Todo
  @model
  @key(
    name: "byTypeCreatedAt"
    fields: ["type", "createdAt"]
    queryField: "byCreatedAt"
  ) {
  id: ID!
  type: String!
  title: String!
  createdAt: AWSDateTime! 
}

・typeというフィールドを追加

・@keyで新しいインデックスとクエリを作っている。(プロパティのfieldsに、typeとcreatedAtフィールドを指定。queryFiledに設定することでbyCreatedAtという名前のクエリが作られる。)

・新しく増えたtypeというフィールドには常に同じ値(例えば"t"とか"post"とか)を入れる。つまりTodoを作る時、以下のようにする。

 // 作成
 const res = await API.graphql({
      query: createTodo,
      variables: {
        input: {
          type: "t", // 同じ値を入れる
          title: this.form.title
        },
      },
    });

そして@keyによりbyCreatedAtクエリが自動生成されているので、そのクエリを使えばcreatedAtでソートされた結果が返ってくる。

        const res = await API.graphql({
          query: byCreatedAt,
          variables: { type: "t", sortDirection: "DESC" },
        }); // これは降順を指定している

これでcreatedAtでソートしたリストを得ることが出来た。

つまるところ、項目の一つを同じ値で埋めて@keyの仕様を回避するわけで、これはいいのか首をかしげたくなるが、DynamoDBの仕様上しかたなさそう。なるべく少ない文字列にしておこう。

@auto

・あと今は@keyのfieldsにはnon nullなフィールドしか指定できない。なのでcreatedAtには"!"をつける必要がある。上記でも以下のようになっている。

...
  createdAt: AWSDateTime! 
}

この部分は今後のAmplifyのアップデートで変わるそうだ。一度null可能にしたがバグが出たので戻ったりしたらしい。今後のアップデートでnull可能か指定できるようになるらしい。

この部分で以下の制約が生まれる。

・"!"を付けnon nullになったcreatedAtは作るとき必ず値を設定しなければならない。なのでTodoを作るときcreatedAtも設定する必要がある。

・フロントで設定できてしまうとユーザーが好きに設定できてしまうのでサーバー側で設定してほしい。

・実現するにはリクエストマッピングテンプレートを設定する。面倒だ。

そんなところへ、自動で値を入れてくれる@autoカスタムディレクションを公開してくれている人がいる。使わせてもらおう!

https://github.com/hirochachacha/graphql-auto-transformer

リンク先の説明どおりに設定する。

npm install graphql-auto-transformer -D
{
    "Version": 5,
    "ElasticsearchWarning": true,
    "transformers": [
        "graphql-auto-transformer"
    ]
}

あとAmplify Consoleでビルドする場合、ビルドコマンドに以下を追記する必要がある。

version: 1
backend:
  phases:
    build:
      commands:
        - npm install -g graphql-auto-transformer
        - '# Execute Amplify CLI with the helper script'
        - amplifyPush --simple

@autoを使うことでTodo型は最終的に以下のようになった。

type Todo
  @model
  @key(
    name: "byTypeCreatedAt"
    fields: ["type", "createdAt"]
    queryField: "byCreatedAt"
  ) {
  id: ID!
  type: String!
  title: String!
  createdAt: AWSDateTime! @auto
}

まとめ

正直ソートしたいだけなのに妙に複雑だ。たぶんDynamoDBのせい。

amplifyは更新が速く(バグも多く)更新で記述が変わる。上記の方法も古くなると思われる。

このあたりのこと公式ドキュメントのガイドページ。またはハンズオン資料、またはissueに書いてある。