Skip to content

Latest commit

 

History

History
382 lines (297 loc) · 18.5 KB

File metadata and controls

382 lines (297 loc) · 18.5 KB

English / 日本語

@codemonger-io/cdk-rest-api-with-spec

Amazon API Gateway (API Gateway)のREST APIとそのOpenAPI定義を@codemonger-io/cdk-rest-api-with-specで一度に記述。

誰のためのライブラリ?

AWS Cloud Development Kit (CDK)のパーツを使ってREST APIとそのOpenAPI定義を一度に書いてしまいたい方には役に立つかもしれません。 詳しくは「背景」をご覧ください。

事前準備

Node.js v12かそれ以降をインストールしてください。 このライブラリはNode.js v22.xで開発しました。

このライブラリはCDKバージョン2 (CDK v2)向けに実装されており、CDKバージョン1には対応していません。 また、aws-cdk-libはv2.201.0以降が必要です。

インストール方法

このレポジトリを依存関係(dependencies)に追加してください。

npm install https://github.com/codemonger-io/cdk-rest-api-with-spec.git#v0.4.1

このライブラリはCDK v2プロジェクトで使用することを想定しており、以下のモジュールはdependenciesではなくpeerDependenciesに含んでいます。

CDK v2プロジェクトで使っている限り、これらを別途インストールする必要はないはずです。

GitHub Packagesからインストールする

mainブランチにコミットがプッシュされるたびに、開発者用パッケージがGitHub Packagesの管理するnpmレジストリにパブリッシュされます。 開発者用パッケージのバージョンは次のリリースバージョンとハイフン(-)と短いコミットハッシュををつなげたもので表現されます。例、0.4.1-abc1234 (abc1234はパッケージをビルドするのに使ったコミット(スナップショット)のコミットハッシュ)。 開発者用パッケージはこちらにあります。

GitHubパーソナルアクセストークンの設定

開発者用パッケージをインストールするには、最低限read:packagesスコープのクラッシックGitHubパーソナルアクセストークン(PAT)を設定する必要があります。 以下、簡単にPATの設定方法を説明します。 より詳しくはGitHubのドキュメントをご参照ください。

PATが手に入ったら以下の内容の.npmrcファイルをホームディレクトリに作成してください。

//npm.pkg.github.com/:_authToken=$YOUR_GITHUB_PAT

$YOUR_GITHUB_PATはご自身のPATに置き換えてください。

プロジェクトのルートディレクトリに以下の内容の.npmrcファイルを作成してください。

@codemonger-io:registry=https://npm.pkg.github.com

これで以下のコマンドで開発者パッケージをインストールできます。

npm install @codemonger-io/cdk-rest-api-with-spec@0.4.1-abc1234

abc1234はインストールしたいスナップショットの短いコミットハッシュに置き換えてください。

始める

aws_apigateway.RestApiの代わりにRestApiWithSpecをインスタンス化してください。

const api = new RestApiWithSpec(this, 'example-api', {
  description: 'Example of RestApiWithSpec',
  openApiInfo: {
    version: '0.0.1',
  },
  openApiOutputPath: 'openapi.json',
  // ... other options
});

CDKスタックを合成すると、OpenAPI定義を含むopenapi.jsonファイルが作成されます。

より詳しくは「使用例」「APIドキュメンテーション」をご参照ください。 実際に動くサンプルもexampleフォルダにあります。

背景

最近、API Gatewayで作成したREST APIのOpenAPI定義を書くべきだなぁという思いが強くなっていました。 私の知る限り、API Gatewayで作ったREST APIのOpenAPI定義を得るには2つの選択肢があります。

  1. 既存のREST APIからOpenAPI定義をエクスポートする
  2. OpenAPI定義をインポートしてREST APIを作成する

1. OpenAPI定義をエクスポートする

追加のドキュメンテーションなしでは、API GatewayからエクスポートされたOpenAPI定義には制約が全然かかっておらず使い物になりません。 REST APIのすべての構成要素に個別にドキュメンテーションリソースを追加しなければなりません。 REST APIの構成要素を作るのと同時にドキュメント作成もできるとよい気がします。

2. OpenAPI定義をインポートする

私はCDKのパーツを使ってAPI GatewayのREST APIを記述するのに慣れています。 以前、素のCloudFormationテンプレートでREST APIを書いていましたが繰り返しが多すぎて嫌気がさしました。 CDKはその苦痛を取り除いてくれました。 素のOpenAPI定義を書くとその苦痛が戻ってくることになるのではないかと思います(試していませんが・・・)。

3番目の選択肢

ということで、CDKのパーツを使いつつREST APIとそのOpenAPI定義を一度に書くことのできる3番目の選択肢を求めています。 そしてこのライブラリが解決策になることを願っています。

SpecRestApiとの違い

CDKはaws_apigateway.SpecRestApiという似たような名前を持つConstructを提供しています。 aws_apigateway.SpecRestApiの目的は既存のOpenAPI定義をインポートしてREST APIを作成することである一方、このライブラリの目的はその逆、REST APIを構築することでOpenAPI定義を作成することです。

使用例

メソッドを記述する

IResourceWithSpec.addMethodメソッドの3番目の引数のsummarydescriptionプロパティを指定することができます。

api.root.addMethod(
  'GET',
  new apigateway.MockIntegration({
    // ... integration settings
  }),
  {
    operationName: 'getRoot',
    summary: 'Get root', // NEW!
    description: 'Returns the root object', // NEW!
    methodResponses: [
      {
        statusCode: '200',
        description: 'successful operation',
      },
    ],
  }
);

operationName, summary, descriptionプロパティはそれぞれOpenAPI定義におけるOperation ObjectoperationId, summary, descriptionプロパティに対応しています。

methodResponsesプロパティの各要素にはdescriptionプロパティを設定することができ、これはOpenAPI定義におけるResponse Objectdescriptionプロパティに対応しています。

リクエストパラメータを記述する

IResourceWithSpec.addMethodメソッドの3番目の引数のrequestParameterSchemasプロパティでリクエストパラメータを記述することできます。

findByStatus.addMethod(
  'GET',
  new apigateway.MockIntegration({
    // ... integration settings
  }),
  {
    operationName: 'findPetsByStatus',
    requestParameterSchemas: { // NEW!
      'method.request.querystring.status': {
        description: 'Status values that need to be considered for filter',
        required: false,
        explode: true,
        schema: {
          type: 'string',
          enum: ['available', 'pending', 'sold'],
          default: 'available',
        },
      },
    },
  },
);

requestParameterSchemasプロパティはKey-ValueペアのマップでrequestParametersプロパティと同じキーを受け付けますが、キーをboolean値ではなくOpenAPI定義におけるParameter Objectを表すオブジェクト(nameinプロパティを除く)にマップします。 Parameter Objectnameinプロパティはキーから導出されます。 ということで上記のrequestParameterSchemasは以下のParameter Objectになります。

[
  {
    name: 'status',
    in: 'query',
    description: 'Status values that need to be considered for filter',
    required: false,
    explode: true,
    schema: {
      type: 'string',
      enum: ['available', 'pending', 'sold'],
      default: 'available',
    },
  },
]

requestParameterSchemasプロパティを指定した場合、requestParametersプロパティを指定する必要はありません。 ベースとなるaws_apigateway.IResource.addMethodに渡されるrequestParametersプロパティはrequestParameterSchemasプロパティからrequestParameters[key] = requestParameterSchemas[key].requiredとなるように生成されます。

requestParameterSchemasプロパティを省略しrequestParametersプロパティを指定した場合、requestParametersプロパティから最低限のParameter Objectを作成します。 以下のオブジェクトをrequestParametersプロパティに指定したとすると、

{
  'method.request.querystring.status': false,
}

以下のようになります。

[
  {
    name: 'status',
    in: 'query',
    required: false,
    schema: {
      type: 'string',
    },
  },
]

requestParametersrequestParameterSchemasプロパティの両方を指定した場合、requestParameterSchemasプロパティが優先されます。

モデル(スキーマ)を定義する

IRestApiWithSpec.addModelメソッドはOpenAPI定義におけるComponents ObjectschemasプロパティにSchema Objectを追加します。 以下は例です。

const petModel = api.addModel('PetModel', {
  description: 'A pet',
  contentType: 'application/json',
  schema: {
    schema: apigateway.JsonSchemaVersion.DRAFT4,
    title: 'pet',
    description: 'A pet',
    type: apigateway.JsonSchemaType.OBJECT,
    properties: {
      id: {
        description: 'ID of the pet',
        type: apigateway.JsonSchemaType.INTEGER,
        format: 'int64',
        example: 123,
      },
      name: {
        description: 'Name of the pet',
        type: apigateway.JsonSchemaType.STRING,
        example: 'Monaka',
      },
    },
  },
});

IRestApiWithSpec.addModelメソッドの2番目の引数のschemaプロパティ(JsonSchemaEx)はOpenAPI定義において等価なSchema Objectに翻訳されます。

IResourceWithSpec.addMethodメソッドの3番目の引数のmethodResponsesプロパティにおけるresponseModelsプロパティから参照したaws_apigateway.IModelはOpenAPI定義でそのaws_apigateway.IModelに対応するスキーマへの参照に置き換えられます。

以下の例におけるmethodResponsesプロパティは、

petId.addMethod(
  'GET',
  new apigateway.MockIntegration({
    // ... integration settings
  }),
  {
    operationName: 'getPetById',
    methodResponses: [
      {
        statusCode: '200',
        description: 'successful operation',
        responseModels: {
          'application/json': petModel,
        },
      },
    ],
  },
);

OpenAPI定義において以下のようなResponses Objectになります。

{
  '200': {
    description: 'successful operation',
    content: {
      'application/json': {
        schema: {
          '$ref': '#/components/schemas/exampleapiPetModel43E308F7'
        },
      },
    },
  },
}

aws_apigateway.IModelに付与されるCloudFormationのリソースIDがaws_apigateway.IModelの参照パスを表現するのに使われます。

aws_apigateway.JsonSchemaの拡張であるJsonSchemaExにはmodelRefという追加のプロパティがあります。 modelRefプロパティを使うとスキーマから別のaws_apigateway.IModelを参照することができます。 以下は配列要素の型を指定するのに別のaws_apigateway.IModelを参照する例です。

const petArrayModel = api.addModel('PetArrayModel', {
  description: 'An array of pets',
  contentType: 'application/json',
  schema: {
    schema: apigateway.JsonSchemaVersion.DRAFT4,
    title: 'petArray',
    description: 'An array of pets',
    type: apigateway.JsonSchemaType.ARRAY,
    items: {
      modelRef: petModel,
    },
  },
});

Authorizerを記述する

augmentAuthorizer関数を使って既存のaws_apigateway.IAuthorizerをOpenAPI定義のためのプロパティで拡張することができます。

const authorizer = augmentAuthorizer(
  new apigateway.TokenAuthorizer(
    this,
    'ExampleAuthorizer',
    {
      handler: new nodejs.NodejsFunction(this, 'authorizer', {
        description: 'Example authorizer',
        runtime: lambda.Runtime.NODEJS_18_X,
      }),
    },
  ),
  {
    type: 'apiKey',
    in: 'header',
    name: 'Authorization',
  },
);

augmentAuthorizer関数の2番目の引数はOpenAPI定義においてAuthorizerを記述するSecurity Scheme Objectです。

APIドキュメンテーション

最新のAPIドキュメンテーションはapi-docs/markdown/index.mdにあります(日本語版はありません)。

開発

依存関係を解決する

pnpm install

ライブラリをビルドする

pnpm build

distフォルダ内で以下のファイルが作成または更新されます。

  • index.js
  • index.js.map
  • index.d.ts

distフォルダは存在しなければ作成されます。

このライブラリのAPIに変更があるとapi-docs/cdk-rest-api-with-spec.api.mdファイルも更新されます。

ドキュメンテーションを生成する

pnpm doc

api-docs/markdownフォルダのコンテンツを置き換えます。