serverless-import-swaggerの紹介
新規サービス開発のプロジェクトの中で、Serverless FrameworkにSwagger形式のAPI定義をインポートする、AKIRA-MIYAKE/serverless-import-swaggerを開発したので、その紹介を行います。
開発の動機
開発中のサービスは、Angular2のSPAをS3に配置してCloudFrontで配信、データ取得などのためのAPIをAWS Lambda + API Gatewayで実装、というサーバレスアーキテクチャを採用しています。
フロントエンドとバックエンドの開発を並行してスムーズに行うことができるように、Swagger形式でAPIを定義しそれに基づいて実装するようにしています。
AWS LambdaへのデプロイやAPI Gatewayとの紐付けをを手作業で行うのは考えられないので、Serverless Frameworkを用いてイベントや権限を定義、CIでの自動デプロイの環境を整えています。
Serverlessでは、このような感じのYAML形式で関数を定義します。
基本的にSwaggerで定義したAPIのメソッドがLambda関数に対応します。Swaggerの定義からServerlessの関数定義を作れたら嬉しいですよね。
でもそんなプラグインはないらしい…ということで開発しました。
開発の上での考え方
Serverlessにはプラグインを追加できる仕組みがありますが、独立したCLIツールとしています。作り始めた時点では、Serverlessのバージョンが0.5から1.0に変わったすぐあとぐらいで、変更が生じる可能性が高かったこと、ビルドやデプロイのプロセスに処理を追加するわけではなかったためです。
他には以下の点を実現できるようにしています。
provider
などの共通項目を別途定義して読み込めること- Swaggerのタグを用いて任意にサービスを分割できること
- 生成されるサービスにプレフィックスを付与できること
- 関数名がAPIに基づいて自動生成されること
- 手動で追加した項目が再度実行時に消えないこと
基本的な使い方
npm install serverless-import-swagger
でインストールします。- Swagger定義ファイルの取り込む対象のメソッドにタグを追加します。
- Swagger定義ファイルをプロジェクトに配置します。
provider
などの共通項目を定義したYAMLファイルを用意します。sis
コマンドを実行することでインポートが実行されます。Serverlessのサービスが存在しない場合は自動的に作成されます。
オプションは以下の通りです。
# 共通オプション -i, --input <path> インポートするSwagger.yamlのファイルを指定します。 (デフォルトは "./swagger.ya?ml" です) -c, --common <path> 共通項目を定義したYAMLファイルを指定します。 (デフォルトは "./serverless.common.ya?ml" です) -o, --out-dir <path> Sserveressサービスの出力先を指定します。 (デフォルトは "./" です) -f, --force このオプションを追加すると、新しい出力で既存の内容を完全に上書きします。 # タグやサービスのプレフィックスに関するオプション -A, --api-prefix <prefix> Swaggerのタグにつけるプレフィックスです。 (デフォルトは "sls" です) -S, --service-prefix <prefix> 出力されるサービスに付与されるプレフィックスです。 (デフォルトはなしです) # ベースパスの扱いに関するオプション -B, --base-path このオプションを追加すると、httpイベントのパスからベースパスが取り除かれます。 # CORSに関するオプション -C, --cors このオプションを追加すると、httpイベントに`cors=true`が追加されます。 -O, --options-method このオプションを追加すると、GETメソッドの関数には`cors=true`がそれ以外のメソッドを含むパスにはoptionsメソッドの関数が追加されます。
サービス分割時のベースパスについて
デフォルトでは、Swaggerのタグに基づいてサービスが分割され、APIのパスがそのままインポートされます。
CloudFormationの制約のため、API数が一定規模を超える場合サービスを分割する必要がありますが、API Gatewayでカスタムドメインに複数のAPIに紐づける場合、ベースパスの指定が必要となります。
そのため、Swaggerで/foo/bar
と定義したAPIがカスタムドメインからアクセスすると/{base-path}/foo/bar
となってしまい、定義と異なってしまいます。
-B, --base-path
オプションを追加することで、Serverlessのイベントのパスからベースパスを除去するようになります。
先ほどの例だと、serverless.ymlでは/foo
が取り除かれて/bar
となり、API Gatewayで/foo
のベースパスでマッピングすることで、Swaggerの定義と一致させることができます。
ただし、この機能を使うと、ベースパスを基準にしたサービスの分割を強制されることとなります。
CORSについて
-C, --cors
オプションをつけるとAPI GatewayのデフォルトのCORSのためのOPTIONSメソッドが用意されます。単純なAPIアクセスを行う場合であれば、このオプションだけで問題はないと思います。
ただし、Cookieや独自ヘッダを用いた通信を行う場合、Access-Control-Allow-Origin
にワイルドカードを指定することができないため、対象のレスポンスを返す関数だけでなく、preflight request
で呼び出されるOPTIONS
も独自に定義する必要があります。
-O, --options-method
オプションを付与すると、SwaggerのAPIのパスの中にGET
以外のメソッドが定義されている場合、自動でそのパスのOPTIONSメソッドに対応する関数を追加します。
追加された関数でOPTIONSのヘッダを独自に定義することで、Cookieや独自ヘッダを用いたCORSを実現することができます。
ベースパスやCORSの部分は現在のプロジェクトやAWSの制約によって用意したものなので、もう少しうまい解決方法があるかもしれませんが…。
開発中のサービスがリリースされ、落ち着いた頃に整理ができればと思います。