見出し画像

Lambdaリゾルバーの返り値について

Lambdaリゾルバーから値を返す方法についての備忘録です。

returnでオブジェクトを返しても上手くいきましたが、ドキュメントではcallback関数を使う方法が書かれています。
そもそもこのcallback関数はどこ由来なのかというと、AmplifyやAppSyncではなく、Lambdaそのものの機能です。

引数にはエラーとレスポンスをとるようです。エラーが先とは奇妙な…。

エラーを返す

Lambadaリゾルバーからcallback("Error message")を呼んでエラーを返すとデフォルトでは以下のようなフォーマットになります。

{
  "data": {
    "deleteProject": null
  },
  "errors": [
    {
      "path": [
        "deleteProject"
      ],
      "data": null,
      "errorType": "Lambda:Unhandled",
      "errorInfo": null,
      "locations": [
        {
          "line": 29,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "Error message."
    }
  ]
}

これでもエラーとわかるので十分機能しますが、errotTypeなど変えたくなります。例えばVTLリゾルバーで$util.unauthorized()を呼んだ時のレスポンスは以下の様なフォーマットです。

{
  "data": {
    "deleteDayOff": null
  },
  "errors": [
    {
      "path": [
        "deleteDayOff"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 41,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access deleteDayOff on type Mutation"
    }
  ]
}

なのでLambdaリゾルバーでも何かしらの認証エラーの時は上のレスポンスと揃えたくなります。
errotTypeを変更するために、Errorオブジェクトを継承したカスタムエラーをつくるなどの記事もありましたが、自分の場合うまくいきませんでした。

結局のところ、ドキュメントにはわかりやすく書かれていませんでしたが、Lambdaリゾルバーの結果を受け取ってクライアントに値を返す処理をしているレスポンスマッピングテンプレートというやつを変更する必要がありました。
Amplify CLIでLambdaリゾルバーを作成するとamplify/backend/api/{api_name}/build/resolvers以下に作成したLambdaリゾルバー名.res.vtlというファイルが生成されます。中身は単に前のリゾルバーの値を返すだけのシンプルなものです。

$util.toJson($ctx.prev.result)

そこでamplify/backend/api/{api_name}/resolvers内に同名のVTLファイルを作成し中身を以下の様に変更して上書きします。(自動生成されるVTLは上書きしないポリシーですが、これに限っては内容がとてもシンプルなので上書きを妥協しました。)

#if( $context.result && $context.result.errorMessage )
    $utils.error($context.result.errorMessage, $context.result.errorType, $context.result.data)
#else
    $utils.toJson($context.result.data)
#end

この様にした上で、Lambdaリゾルバーから以下のようにcallbackを呼んでみます。

return callback(null, {
    errorType: "Unauthorized", 
    errorMessage: "Not Authorized to access deleteProject on type Mutation"
});

無事に意図したエラーフォーマットで返ってきました。

{
  "data": {
    "deleteProject": null
  },
  "errors": [
    {
      "path": [
        "deleteProject"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 29,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access deleteProject on type Mutation"
    }
  ]
}

ちなみにレスポンスマッピングテンプレートを変更しているので、エラーなしでデータを返したい場合は以下の様にdataキーを使って返す必要があります。

return callback(null, {
    data: {
        id: 1234,
        name: "project name",
        ...(略)
    }
})

今回はここまで。
ではまた次の記事で。

(追記)
エラーのない場合はそのままオブジェクトを返す方が楽なのでレスポンスマッピングリゾルバーをこの様に変更しました。

#if( $context.result && $context.result.errorMessage )
    $utils.error($context.result.errorMessage, $context.result.errorType, $context.result.data)
#else
    $utils.toJson($context.result)
#end

エラーの場合はオブジェクトはdataに入れて、そうでない場合はそのまま返す。

もしこの記事があなたのお役に立てたなら幸いです。 よろしければサポートをお願いします。今後の制作資金にさせていただきます!