AWS APIGateway × OpenAPI (2. Proxy編)
2022-07-26
OpenAPI定義をCDKのREST API定義に統合するライブラリを開発しています。 これはライブラリの開発過程を紹介するシリーズのブログ投稿第2弾です。
背景
本シリーズの最初のブログ投稿で、以下の課題を残しました。
RestApiとの互換性はどうやって実現するか?
RestApiを再利用しないのは大変すぎるので、サブクラスを書くことにします。
インターフェイスの定義
AWS Cloud Development Kit (CDK)のAPIを拡張する前に、最低限のインターフェイスを定義しましょう。 以下のセクションに現れるインターフェイスやコードはシンプルにするために私のライブラリから部分的に抽出し改変したものなのでご注意ください。 完全な定義はGitHubレポジトリにあります。
IRestApiWithSpec
IRestApiWithSpecはIRestApiの拡張です。
IResourceWithSpec
IResourceWithSpecはResourceの拡張*です。
* IResource.addResourceを正しく拡張するには、IResourceWithSpecはIResourceではなくResourceを実装していなくてはなりません。
一方で、IRestApiWithSpec.rootはResourceではなくIResourceを実装しなければならないので、IRestApiWithSpecのroot: IResourceWithSpec定義は限定的すぎます。
しかし、Resourceの公開インターフェイスとIResourceは私の知る限り同じなので問題にはならないはずです。
ResourceOptionsWithSpec
ResourceOptionsWithSpecはResourceOptionsの拡張です。
MethodOptionsWithSpec
MethodOptionsWithSpecはMethodOptionsの拡張です。
BasePrameterObjectは外部ライブラリOpenApi3-TS[1]から借りてきました。
当初はMethodOptions.requestParametersの定義を以下のようにオーバーライドするつもりだったのですが(前のブログ投稿の例参照)・・・
requestParameters?:;
MethodOptionsWithSpecをMethodOptionsに代入できなくしてしまうのでこの試みは失敗しました。
ということで回避策としてrequestParameterSchemasという新しいプロパティを導入しました。
RestApiを拡張する
RestApiを拡張するのは単純です。
言語機能(extends)を使いRestApiのプロパティをオーバーライドするだけです。
単純化のためRestApiWithSpecPropsの詳細は省きます。
ご興味がありましたらGitHubレポジトリをご参照ください。
ここで、疑問はどのようにrootを実装するかです。
親クラス(RestApi)のrootをラップするのが良さそうです。
ではどうやってラップしましょうか?
ほとんどのリクエストをRestApiのrootに転送するだけのラッパークラスを書きましょうか?
大量のBoilerplate(定型的なコード)が発生しそうです。
それならProxyがニーズにもっとマッチしそうです。
rootにProxyをかませる
root用のProxyを書くのはとても簡単です。
ハンドラオブジェクトのget関数を実装するだけです。
// root: IResource
;
残念ながら、TypeScriptは上記コードでProxyをかませたオブジェクトをIResourceWithSpecだと認識せずエラーになります。
ProxyコンストラクタのデフォルトのTypeScript定義が以下のようになっているからです。
declare global
この問題に対処するため、Proxyコンストラクタがtargetとは違う型を生成することができるように定義を拡張できます。
StackOverflowのこちらの投稿[2]がまさに答えです。
ということで以下の宣言を追加するとTypeScriptエラーが解決します。
declare global
Resourceを拡張する
Resourceを生成するたびに、節「rootにProxyをかませる」で記述したようにProxyでラップする必要があります。
ということで既存のどんなIResourceでもIResourceWithSpecの機能でラップできるファクトリーメソッドaugmentResourceを導入しました。
このファクトリーメソッドはrootにも適用可能です。
上記のコードはシンプルにしてあり、実際の定義はGitHubレポジトリにあります。
まとめ
このブログ投稿では、CDK APIの拡張を簡単にするためにProxyを導入しました。
Proxyに関するTypeScriptのエラーを回避するトリックも紹介しました。
次のブログ投稿では、いつOpenAPI定義ファイルを出力するかという課題に挑む予定です。
Reference
-
OpenAPI 3の仕様そのものに対するTypeScriptバインディング。
-
How to use Proxy
with a different type than T as argument? - Answer