Skip to content

Smithy TypeScript API

Smithyは、モデル駆動方式でAPIを作成するためのプロトコル非依存のインターフェース定義言語です。

Smithy TypeScript APIジェネレーターは、サービス定義にSmithyを使用し、実装にSmithy TypeScript Server SDKを使用して新しいAPIを作成します。このジェネレーターは、AWS API Gateway REST API経由で公開されるAWS LambdaにサービスをデプロイするためのCDKまたはTerraformのインフラストラクチャコードを提供します。Smithyモデルからの自動コード生成により、型安全なAPI開発を実現します。生成されたハンドラーは、ロギング、AWS X-Rayトレーシング、CloudWatch Metricsを含む可観測性のためにAWS Lambda Powertools for TypeScriptを使用します。

新しいSmithy TypeScript APIは2つの方法で生成できます:

このジェネレーターを実行@aws/nx-plugin:ts#api

pnpm nx g @aws/nx-plugin:ts#api --framework=smithy
コマンドを組み立てる10

必須

framework = smithy

ジェネレーターオプション10 オプション
name必須string

APIの名前(必須)。クラス名とファイルパスの生成に使用されます。

frameworkenumデフォルト: trpc

使用するAPIフレームワーク。

trpcsmithy
integrationPatternenumデフォルト: isolated

API用にAPI Gateway統合を生成する方法。isolated(デフォルト)またはsharedから選択します。

isolatedshared
authenumデフォルト: iam

APIの認証に使用する方法。iam(デフォルト)、cognito、customから選択します。

iamcognitocustom
directorystringデフォルト: packages

アプリケーションを保存するディレクトリ。

iacenumデフォルト: inherit

優先するIaCプロバイダー。デフォルトでは初期選択から継承されます。

inheritcdkterraform
infraenumデフォルト: rest-lambda

このAPIをデプロイするために使用するインフラストラクチャのタイプ。

rest-lambdanone
namespacestringframework = smithy

Smithy APIの名前空間(smithyフレームワークにのみ適用されます)。デフォルトはモノレポのスコープです

subDirectorystring

プロジェクトが配置されるサブディレクトリ。デフォルトではプロジェクト名になります。

preferInstallDependenciesbooleanデフォルト: true

ジェネレーター実行後に依存関係のインストールを優先するかどうか。複数のジェネレーターをバッチ処理する際にインストールを延期する場合は false に設定します(後続のジェネレーターが Nx プロジェクトグラフを計算できるよう、必要に応じてインストールは実行されます)。最後に一度だけインストールします。

ジェネレーターは<directory>/<api-name>ディレクトリに2つの関連プロジェクトを作成します:

  • Directorymodel/ Smithyモデルプロジェクト
    • project.json プロジェクト設定とビルドターゲット
    • smithy-build.json Smithyビルド設定
    • ssdk.rolldown.config.mjs 生成されたTypeScript Server SDKをバンドル
    • Directorysrc/
      • main.smithy メインサービス定義
      • Directoryoperations/
        • echo.smithy サンプルオペレーション定義
  • Directorybackend/ TypeScriptバックエンド実装
    • package.json プロジェクトのパッケージ名と依存関係を定義するプロジェクトマニフェスト
    • project.json プロジェクト設定とビルドターゲット
    • rolldown.config.ts バンドル設定
    • tsconfig.json TypeScript設定
    • tsconfig.lib.json ライブラリソース用のTypeScript設定
    • tsconfig.spec.json テスト用のTypeScript設定
    • vitest.config.mts Vitest設定
    • Directorysrc/
      • index.ts パッケージエントリーポイント
      • handler.ts AWS Lambdaハンドラー
      • local-server.ts ローカル開発サーバー
      • service.ts サービス実装
      • context.ts サービスコンテキスト定義
      • Directoryoperations/
        • echo.ts サンプルオペレーション実装
      • Directorygenerated/ 生成されたTypeScript SDK(ビルド時に作成)
        • …

このジェネレーターは選択したiacに基づいてインフラストラクチャコードを作成するため、関連するCDKコンストラクトまたはTerraformモジュールを含むpackages/commonにプロジェクトを作成します。

共通のインフラストラクチャコードプロジェクトは次のように構成されています:

  • Directorypackages/common/constructs
    • Directorysrc
      • Directoryapp/ プロジェクト/ジェネレーター固有のインフラストラクチャ用のコンストラクト
        • Directoryapis/
          • <project-name>.ts APIをデプロイするためのCDKコンストラクト
      • Directorycore/ appのコンストラクトで再利用される汎用コンストラクト
        • Directoryapi/
          • rest-api.ts REST APIをデプロイするためのCDKコンストラクト
          • utils.ts APIコンストラクト用のユーティリティ
      • index.ts appからコンストラクトをエクスポートするエントリーポイント
    • project.json プロジェクトビルドターゲットと設定

デプロイされたSmithy APIは次のアーキテクチャを持ち、API Gatewayステージの前にAWS WAFv2 Web ACLが配置されます:

Loading the diagram…

Smithyでのオペレーションの定義

Section titled “Smithyでのオペレーションの定義”

オペレーションはモデルプロジェクト内のSmithyファイルで定義されます。メインサービス定義はmain.smithyにあります:

$version: "2.0"
namespace your.namespace
use aws.protocols#restJson1
use smithy.framework#ValidationException
@title("YourService")
@restJson1
service YourService {
version: "1.0.0"
operations: [
Echo,
// Add your operations here
]
errors: [
ValidationException
]
}

個々のオペレーションはoperations/ディレクトリ内の別々のファイルで定義されます:

$version: "2.0"
namespace your.namespace
@http(method: "POST", uri: "/echo")
operation Echo {
input: EchoInput
output: EchoOutput
}
structure EchoInput {
@required
message: String
foo: Integer
bar: String
}
structure EchoOutput {
@required
message: String
}

同じデータ型を共有する複数のSmithy APIがある場合、各モデルで重複させるのではなく、シェイプライブラリでそれらの型を一度定義できます。シェイプライブラリは、サービスを持たないSmithyプロジェクトで、再利用可能なシェイプのみを持ち、任意の数のSmithyプロジェクトが依存できます。

smithy#projectジェネレーターで生成します:

このジェネレーターを実行@aws/nx-plugin:smithy#project

pnpm nx g @aws/nx-plugin:smithy#project --name=my-shapes --type=shapes
コマンドを組み立てる7

必須

APIのモデルはuseでそのシェイプを参照できます:

$version: "2.0"
namespace com.example.api
use com.example.shared#Customer
structure GetCustomerOutput {
@required
customer: Customer
}

シェイプライブラリを作成し、APIのモデルの依存関係として接続する方法については、Smithyプロジェクトガイドを参照してください。

TypeScriptでのオペレーションの実装

Section titled “TypeScriptでのオペレーションの実装”

オペレーション実装はバックエンドプロジェクトのsrc/operations/ディレクトリにあります。各オペレーションは、TypeScript Server SDKから生成された型を使用して実装されます(Smithyモデルからビルド時に生成されます)。

import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input) => {
// Your business logic here
return {
message: `Echo: ${input.message}` // type-safe based on your Smithy model
};
};

オペレーションはsrc/service.tsのサービス定義に登録する必要があります:

import { ServiceContext } from './context.js';
import { YourServiceService } from './generated/ssdk/index.js';
import { Echo } from './operations/echo.js';
// Import other operations here
// Register operations to the service here
export const Service: YourServiceService<ServiceContext> = {
Echo,
// Add other operations here
};

context.tsでオペレーションの共有コンテキストを定義できます:

export interface ServiceContext {
// Powertools tracer, logger and metrics are provided by default
tracer: Tracer;
logger: Logger;
metrics: Metrics;
// Add shared dependencies, database connections, etc.
dbClient: any;
userIdentity: string;
}

このコンテキストはすべてのオペレーション実装に渡され、データベース接続、設定、ロギングユーティリティなどのリソースを共有するために使用できます。

AWS Lambda Powertoolsによる可観測性

Section titled “AWS Lambda Powertoolsによる可観測性”

ジェネレーターは、Middyミドルウェアによる自動コンテキストインジェクションを使用して、AWS Lambda Powertoolsを使用した構造化ロギングを設定します。

handler.ts
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
.use(captureLambdaHandler(tracer))
.use(injectLambdaContext(logger))
.use(logMetrics(metrics))
.handler(lambdaHandler);

コンテキスト経由でオペレーション実装からロガーを参照できます:

operations/echo.ts
import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
ctx.logger.info('Your log message');
// ...
};

AWS X-RayトレーシングはcaptureLambdaHandlerミドルウェアによって自動的に設定されます。

handler.ts
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
.use(captureLambdaHandler(tracer))
.use(injectLambdaContext(logger))
.use(logMetrics(metrics))
.handler(lambdaHandler);

オペレーション内でトレースにカスタムサブセグメントを追加できます:

operations/echo.ts
import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
// Creates a new subsegment
const subsegment = ctx.tracer.getSegment()?.addNewSubsegment('custom-operation');
try {
// Your logic here
} catch (error) {
subsegment?.addError(error as Error);
throw error;
} finally {
subsegment?.close();
}
};

CloudWatchメトリクスはlogMetricsミドルウェアによって各リクエストに対して自動的に収集されます。

handler.ts
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
.use(captureLambdaHandler(tracer))
.use(injectLambdaContext(logger))
.use(logMetrics(metrics))
.handler(lambdaHandler);

オペレーション内でカスタムメトリクスを追加できます:

operations/echo.ts
import { MetricUnit } from '@aws-lambda-powertools/metrics';
import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
ctx.metrics.addMetric("CustomMetric", MetricUnit.Count, 1);
// ...
};

Smithyは組み込みのエラーハンドリングを提供します。Smithyモデルでカスタムエラーを定義できます:

@error("client")
@httpError(400)
structure InvalidRequestError {
@required
message: String
}

そして、オペレーション/サービスに登録します:

operation MyOperation {
...
errors: [InvalidRequestError]
}

次に、TypeScript実装でそれらをスローします:

import { InvalidRequestError } from '../generated/ssdk/index.js';
export const MyOperation: MyOperationHandler<ServiceContext> = async (input) => {
if (!input.requiredField) {
throw new InvalidRequestError({
message: "Required field is missing"
});
}
return { /* success response */ };
};

呼び出し元ユーザーへのアクセス

Section titled “呼び出し元ユーザーへのアクセス”

APIが認証によって保護されている場合、オペレーションは誰が呼び出しているかを知る必要があることがよくあります。推奨されるアプローチは、ハンドラーで呼び出し元のIDを一度解決し、特定のオペレーションで使用するためにサービスコンテキストを通じて渡すことです。

未承認のケースをSmithyエラーとしてモデル化し、適切な403レスポンスにシリアライズされるようにします。モデルに追加します(例:model/src/operations/errors.smithy)、そしてIDを必要とする任意のオペレーションで参照します:

$version: "2.0"
namespace your.namespace
/// Thrown when the calling user cannot be determined
@error("client")
@httpError(403)
structure UnauthorizedError {
@required
message: String
}

まず、src/context.tsのサービスコンテキストで解決されたIDを公開します。UnauthorizedErrorがオペレーション内からスローされるように(Server SDKがそれを403にシリアライズする場所)、ハンドラーからではなく、関数として提供します:

import { Logger } from '@aws-lambda-powertools/logger';
import { Metrics } from '@aws-lambda-powertools/metrics';
import { Tracer } from '@aws-lambda-powertools/tracer';
export interface Identity {
sub: string;
username: string;
}
/**
* Context provided to all operations.
*/
export interface ServiceContext {
tracer: Tracer;
logger: Logger;
metrics: Metrics;
getIdentity: () => Promise<Identity>;
}

次に、src/identity.tsにリゾルバーを記述します。呼び出し元を特定できない場合はUnauthorizedErrorをスローします。実装は選択したauthメソッドによって異なります:

auth = iam

IAM認証の場合、API Gatewayイベントから抽出されたsubを使用してCognitoで呼び出し元を検索します。検索にはCognito Identity Providerクライアントを使用しますが、これは生成されたSmithyバックエンドの依存関係ではないため、最初にバックエンドプロジェクトにインストールします:

Terminal window
pnpm add @aws-sdk/client-cognito-identity-provider@3.1146.0 --filter my-api
import { CognitoIdentityProvider } from '@aws-sdk/client-cognito-identity-provider';
import type { APIGatewayProxyEvent } from 'aws-lambda';
import { Identity } from './context.js';
import { UnauthorizedError } from './generated/ssdk/index.js';
const cognito = new CognitoIdentityProvider();
export const getIdentity = async (
event: APIGatewayProxyEvent,
): Promise<Identity> => {
const cognitoAuthenticationProvider =
event.requestContext?.identity?.cognitoAuthenticationProvider;
let sub: string | undefined = undefined;
if (cognitoAuthenticationProvider) {
const providerParts = cognitoAuthenticationProvider.split(':');
sub = providerParts[providerParts.length - 1];
}
if (!sub) {
throw new UnauthorizedError({
message: 'Unable to determine calling user',
});
}
const { Users } = await cognito.listUsers({
// Assumes user pool id is configured in lambda environment
UserPoolId: process.env.USER_POOL_ID!,
Limit: 1,
Filter: `sub="${sub}"`,
});
if (!Users || Users.length !== 1) {
throw new UnauthorizedError({
message: `No user found with subjectId ${sub}`,
});
}
return { sub, username: Users[0].Username! };
};
auth = cognito

auth: 'cognito'の場合、API Gateway Cognito User Poolsオーソライザーは、呼び出し元がAuthorizationヘッダーで提供するJWTを検証し、検証されたクレームをevent.requestContext.authorizer.claimsのイベントに配置します:

import type { APIGatewayProxyEvent } from 'aws-lambda';
import { Identity } from './context.js';
import { UnauthorizedError } from './generated/ssdk/index.js';
export const getIdentity = async (
event: APIGatewayProxyEvent,
): Promise<Identity> => {
const claims = event.requestContext?.authorizer?.claims as
| Record<string, string>
| undefined;
const sub = claims?.sub;
const username = claims?.username;
if (!sub || !username) {
throw new UnauthorizedError({
message: 'Unable to determine calling user',
});
}
return { sub, username };
};

次に、src/handler.tsでリゾルバーをコンテキストに接続します:

import { Service } from './service.js';
import { getIdentity } from './identity.js';
// ...
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
getIdentity: () => getIdentity(event),
});

getIdentityはServiceContextの必須フィールドであり、上記の注意が示すようにコンテキストは両方のエントリーポイントで構築されるため、src/local-server.tsにも必要です。ローカルサーバーの前にはAPI Gatewayオーソライザーがないため、ローカル開発用のスタブIDを提供します:

const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
getIdentity: async () => ({ sub: 'local', username: 'local' }),
});

これで、オペレーション内で解決されたIDを使用できます。例えばsrc/operations/echo.tsで:

import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
const identity = await ctx.getIdentity();
return { message: `${identity.username} says ${input.message}` };
};

SmithyモデルプロジェクトはSmithy CLIを使用してSmithyアーティファクトをビルドし、TypeScript Server SDKを生成します:

Terminal window
pnpm nx build <model-project>

macOSとLinuxでは、CLIはmiseによって解決され、ビルドがオンデマンドで取得するため、インストールするものはありません — 最初にビルドするときにピン留めされたバージョンをダウンロードしてキャッシュします。

このプロセスは:

  1. Smithyモデルをコンパイルして検証します
  2. SmithyモデルからOpenAPI仕様を生成します
  3. 型安全なオペレーションインターフェースを持つTypeScript Server SDKを作成します
  4. ビルドアーティファクトを出力してdist/<model-project>/build/に配置します

バックエンドプロジェクトはコンパイル中に生成されたSDKを自動的にコピーします:

Terminal window
pnpm nx copy-ssdk <backend-project>

miseはnpmにWindowsパッケージを公開していないため、WindowsではSmithy CLIは自分でインストールする前提条件です。Smithy CLIインストールガイドに従って一度インストールし(例:winget install smithyまたはscoop install smithy)、smithyがPATHにあることを確認してください。Windowsで生成されたSmithyプロジェクトは、miseを介してではなくsmithyを直接実行します。

または、WSL内で開発すると、ビルドはLinuxパスを実行し、miseがCLIを解決します — インストールするものはありません。

Windowsで生成されたプロジェクトは、smithyを直接呼び出すcompileターゲットをコミットするため、それに取り組む他の誰か — macOSやLinuxを含む — もPATHにSmithy CLIが必要です。それらのマシンが代わりにmiseを介してCLIを解決するには、以下で説明するように、ターゲットをmiseコマンドに切り替えます。

macOSとLinuxはmiseを介してCLIを解決し、WindowsはグローバルにインストールされたCLIを使用しますが、モデルプロジェクトのproject.jsonのcompileターゲットのコマンドを編集することで、任意のプラットフォームでどちらかを選択できます。

miseの代わりにグローバルにインストールされたSmithy CLIを使用するには、miseプレフィックスを単なるsmithyに置き換えます:

project.json
{
"targets": {
"compile": {
"options": {
"commands": ["... npx -y mise@<version> exec smithy@<version> -- smithy build ..."]
"commands": ["... smithy build ..."]
}
}
}
}

miseがCLIを解決するように戻すには、npx -y mise@<version> exec smithy@<version> --プレフィックスを復元します。

ジェネレーターは、Rolldown を使用してデプロイメントパッケージを作成する bundle ターゲットを自動的に設定します:

Terminal window
pnpm nx bundle <project-name>

Rolldown の設定は rolldown.config.ts にあり、生成するバンドルごとにエントリーがあります。Rolldown は、定義されている場合、複数のバンドルを並列で作成することを管理します。

ジェネレーターはホットリロード機能を持つローカル開発サーバーを設定します:

Terminal window
pnpm nx serve <backend-project>

ジェネレーターは選択したiacに基づいてCDKまたはTerraformインフラストラクチャを作成します。

APIをデプロイするためのCDKコンストラクトはcommon/constructsフォルダにあります:

import { MyApi } from '@my-scope/common-constructs';
export class ExampleStack extends Stack {
constructor(scope: Construct, id: string) {
// Add the API to your stack
const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
}
}

これにより次が設定されます:

  1. SmithyサービスのAWS Lambda関数
  2. 関数トリガーとしてのAPI Gateway REST API
  3. IAMロールと権限
  4. CloudWatchロググループ
  5. X-Rayトレーシング設定
auth = cognito
auth = custom

REST API の場合、生成されたコンストラクトはデフォルトで AWS WAFv2 Web ACL を API Gateway ステージに関連付けます。Web ACL は AWS マネージド型デフォルトルールセット(AWSManagedRulesCommonRuleSet および AWSManagedRulesKnownBadInputsRuleSet)を使用し、OWASP Top 10 を含む一般的な Web エクスプロイトに対する保護を提供します。WAF リクエストログは CloudWatch Logs グループに書き込まれます。

生成された rest-api コンストラクトを編集して、ルールを追加、削除、または調整できます(例えば、レートベースルールや追加のマネージド型ルールグループを追加するなど)。

オプトアウトするには(例えば、独自の Web ACL をアタッチする場合)、enableWaf を false に設定します:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
enableWaf: false,
});

REST APIの場合、生成されたインフラストラクチャはデフォルトでアクセスログを有効にし、リクエストごとに1行の構造化JSONを専用のCloudWatch Logsグループに書き込みます。ログループは顧客管理のKMSキーで暗号化され、1年間保持されます。

API Gatewayは、アカウントレベルのCloudWatch Logsロールを使用してアクセスログを書き込みます。このロールはAWS::ApiGateway::Account設定で構成されており、これはリージョンごと、アカウントごとのシングルトンです。つまり、リージョン内のすべてのREST APIに対して1つのロールしか存在しません。独立してデプロイされる複数のスタック間でこれを安全に管理するため、生成されたインフラストラクチャは次のようになっています:

  • 共有CloudWatch Logsロールを作成し、動作中のロールがまだ設定されていない場合にのみアカウントに構成するため、デプロイメントが他のスタックが所有するロールを上書きすることはありません。
  • ティアダウン時にアカウント設定をそのままにするため、1つのスタックを破棄してもリージョン内の他のREST APIのログ記録が無効になることはありません。

アカウントロールはApiGatewayAccountコンストラクトによって管理されます。これはApiGatewayAccount.ensure(scope)を介して解決されるスタックスコープのシングルトンです。各REST APIのステージはこれに依存しており、ロールはLambdaバックのカスタムリソースによって構成されます。

アクセスログフォーマットは、APIが拡張するRestApiコンストラクトによって設定されます。カスタマイズするには、生成されたpackages/common/constructs/src/app/apis/my-api.ts内でdeployOptionsをsuperに渡し、コンストラクトがすでに設定しているtracingEnabledを保持します:

packages/common/constructs/src/app/apis/my-api.ts
super(scope, id, {
apiName: 'MyApi',
// ...
deployOptions: {
tracingEnabled: true,
accessLogFormat: AccessLogFormat.clf(),
},
...props,
});

AccessLogFormatはaws-cdk-lib/aws-apigatewayからインポートされます。設定しないものはすべて、コンストラクトのデフォルト(標準フィールドを持つJSON形式)を保持します。

デフォルトでは、APIはAPI Gatewayが生成するエンドポイント(https://<id>.execute-api.<region>.amazonaws.com/...)から提供されます。代わりに独自のドメインから提供するには、ドメイン名とそのACM証明書を指定します。カスタムドメインはデフォルトでリージョナルエンドポイントを使用するため、証明書はAPIと同じリージョンにある必要があります。デフォルトでは最小TLSバージョン1.2が使用されます。

カスタムドメインが設定されている場合、ランタイム設定のAPIのURLはカスタムドメインURLになるため、ウェブサイトや他のクライアントは生成されたエンドポイントではなく、あなたのドメインを呼び出します。

生成されたpackages/common/constructs/src/app/apis/my-api.tsで、ドメインをsuperに渡します:

infra = rest-lambda
packages/common/constructs/src/app/apis/my-api.ts
import { Certificate } from 'aws-cdk-lib/aws-certificatemanager';
super(scope, id, {
apiName: 'MyApi',
// ...
domainName: {
domainName: 'api.example.com',
certificate: Certificate.fromCertificateArn(
scope,
'MyApiCertificate',
'arn:aws:acm:<region>:123456789012:certificate/...',
),
},
...props,
});

APIはステージパスプレフィックス(/prod/)なしで、ドメインのルートから提供されます。代わりにパスの下で提供するには、domainNameにbasePathを設定します。ランタイム設定のURLにはそれが含まれます。basePathはCDKでのみ利用可能です。Terraformモジュールは常にドメインのルートでAPIを提供します。

infra = http-lambda
packages/common/constructs/src/app/apis/my-api.ts
import { DomainName } from 'aws-cdk-lib/aws-apigatewayv2';
import { Certificate } from 'aws-cdk-lib/aws-certificatemanager';
super(scope, id, {
apiName: 'MyApi',
// ...
defaultDomainMapping: {
domainName: new DomainName(scope, 'MyApiDomainName', {
domainName: 'api.example.com',
certificate: Certificate.fromCertificateArn(
scope,
'MyApiCertificate',
'arn:aws:acm:<region>:123456789012:certificate/...',
),
}),
},
...props,
});

カスタムドメインが設定されている場合、スタックはMyApiDomainNameAlias(DNSレコードを指すドメイン名)とMyApiDomainNameAliasHostedZoneIdを出力します。

最後に、ドメインのDNSレコードを作成します。任意のDNSプロバイダーで、上記のドメイン名出力を指すCNAMEレコードを作成します。ドメインがRoute 53でホストされている場合は、代わりにホストゾーンID出力を使用してエイリアスレコードを作成できます。これはゾーンapex(例:example.com)でも機能します。

REST/HTTP API CDK コンストラクトは、各オペレーションのインテグレーションを定義するための型安全なインターフェースを提供するように構成されています。

デフォルトインテグレーション

Section titled “デフォルトインテグレーション”

静的な defaultIntegrations を使用して、各オペレーションに個別の AWS Lambda 関数を定義するデフォルトパターンを利用できます:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});

インテグレーションへのアクセス

Section titled “インテグレーションへのアクセス”

API コンストラクトの integrations プロパティを介して、基礎となる AWS Lambda 関数に型安全な方法でアクセスできます。例えば、API が sayHello という名前のオペレーションを定義していて、この関数にいくつかの権限を追加する必要がある場合、次のように実行できます:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
// sayHello is typed to the operations defined in your API
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({
effect: Effect.ALLOW,
actions: [...],
resources: [...],
}));

API が shared パターンを使用している場合、共有ルーター Lambda は api.integrations.$router として公開されます:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
api.integrations.$router.handler.addEnvironment('LOG_LEVEL', 'DEBUG');

withOverrides を介してすべてのオペレーションをオーバーライドした場合、デフォルトのルーターインテグレーションを使用するオペレーションが残っていないため、$router は使用できなくなることに注意してください。

デフォルトオプションのカスタマイズ

Section titled “デフォルトオプションのカスタマイズ”

各デフォルトインテグレーションの Lambda 関数を作成する際に使用されるオプションをカスタマイズしたい場合は、withDefaultOptions メソッドを使用できます。例えば、すべての Lambda 関数を Vpc 内に配置したい場合:

const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withDefaultOptions({
vpc,
})
.build(),
});

オペレーションごとのオプションのカスタマイズ

Section titled “オペレーションごとのオプションのカスタマイズ”

_特定の_オペレーションのデフォルトインテグレーションを作成するために使用されるオプションをカスタマイズする(他のオペレーションに影響を与えずに)には、withOperationOptions メソッドを使用できます。例えば、1 つのオペレーションだけの Lambda 関数タイムアウトを増やしたい場合:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOperationOptions({
sayHello: {
timeout: Duration.seconds(60),
},
})
.build(),
});
// The selected operations remain default integrations, so they're still typed accordingly:
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ ... }));

指定したオプションは、デフォルトインテグレーションオプション(および withDefaultOptions で設定されたオプション)とマージされます。withOverrides で置き換えたオペレーションにはオプションを指定できないことに注意してください。これらはデフォルトインテグレーションを使用しなくなるためです。

withOperationOptions と withOverrides の両方で同じオペレーションをターゲットにすると、呼び出す順序に関係なく型エラーが発生します。

インテグレーションのオーバーライド

Section titled “インテグレーションのオーバーライド”

withOverrides メソッドを使用して、特定のオペレーションのインテグレーションをオーバーライドすることもできます。各オーバーライドは、HTTP または REST API の適切な CDK インテグレーションコンストラクトに型付けされた integration プロパティを指定する必要があります。withOverrides メソッドも型安全です。例えば、getDocumentation API をオーバーライドして、外部ウェブサイトでホストされているドキュメントを指すようにしたい場合、次のように実現できます:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getDocumentation: {
integration: new HttpIntegration('https://example.com/documentation'),
},
})
.build(),
});

また、オーバーライドされたインテグレーションは、api.integrations.getDocumentation を介してアクセスする際に handler プロパティを持たなくなることに気付くでしょう。

インテグレーションに追加のプロパティを追加することもでき、それらも適切に型付けされます。これにより、他のタイプのインテグレーションを抽象化しながら型安全性を維持できます。例えば、REST API 用の S3 インテグレーションを作成し、後で特定のオペレーションのバケットを参照したい場合、次のように実行できます:

const storageBucket = new Bucket(this, 'Bucket', { ... });
const apiGatewayRole = new Role(this, 'ApiGatewayS3Role', {
assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
});
storageBucket.grantRead(apiGatewayRole);
const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getFile: {
bucket: storageBucket,
integration: new AwsIntegration({
service: 's3',
integrationHttpMethod: 'GET',
path: `${storageBucket.bucketName}/{fileName}`,
options: {
credentialsRole: apiGatewayRole,
requestParameters: {
'integration.request.path.fileName': 'method.request.querystring.fileName',
},
integrationResponses: [{ statusCode: '200' }],
},
}),
options: {
requestParameters: {
'method.request.querystring.fileName': true,
},
methodResponses: [{
statusCode: '200',
}],
}
},
})
.build(),
});
// Later, perhaps in another file, you can access the bucket property we defined
// in a type-safe manner
api.integrations.getFile.bucket.grantRead(...);

オーソライザーのオーバーライド

Section titled “オーソライザーのオーバーライド”

インテグレーションで options を指定して、オーソライザーなどの特定のメソッドオプションをオーバーライドすることもできます。例えば、getDocumentation オペレーションに Cognito 認証を使用したい場合:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getDocumentation: {
integration: new HttpIntegration('https://example.com/documentation'),
options: {
authorizer: new CognitoUserPoolsAuthorizer(...) // for REST, or HttpUserPoolAuthorizer for an HTTP API
}
},
})
.build(),
});

必要に応じて、デフォルトインテグレーションを使用せず、各オペレーションに直接インテグレーションを提供することもできます。これは、例えば各オペレーションが異なるタイプのインテグレーションを使用する必要がある場合や、新しいオペレーションを追加する際に型エラーを受け取りたい場合に便利です:

new MyApi(this, 'MyApi', {
integrations: {
sayHello: {
integration: new LambdaIntegration(...),
},
getDocumentation: {
integration: new HttpIntegration(...),
},
},
});

生成された API は 2 つのインテグレーションパターンをサポートしています:

  • isolated は、オペレーションごとに 1 つの Lambda 関数を作成します。これは API のデフォルトで推奨されるオプションです。
  • shared は、単一のデフォルトルーター Lambda を作成し、特定のインテグレーションをオーバーライドしない限り、すべてのオペレーションでそれを再利用します。

isolated は、オペレーションごとにより細かい権限と設定を提供し、ログとトレースのより良い分離も提供します。shared は、使用頻度の低い API でコールドスタートに遭遇する可能性を減らします。

インテグレーションパターンは、API コンストラクトを更新することで CDK でいつでも変更できます。例えば、pattern を 'shared' に設定すると、オペレーションごとに 1 つではなく、単一の関数が作成されます:

packages/common/constructs/src/app/apis/my-api.ts
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => {
...
return IntegrationBuilder.rest({
pattern: 'shared',
...
});
};
}
iac = terraform, infra = rest-lambda

オペレーションはSmithyで定義されているため、コード生成を使用してCDKコンストラクトに型安全な統合のためのメタデータを提供します。

generate:<ApiName>-metadataターゲットが共通コンストラクトのproject.jsonに追加され、このコード生成を容易にします。これはpackages/common/constructs/src/generated/my-api/metadata.gen.tsのようなファイルを出力します。これはビルド時に生成されるため、バージョン管理では無視されます。

auth = iam

IAM認証を選択した場合、grantInvokeAccessメソッドを使用してAPIへのアクセスを付与できます:

api.grantInvokeAccess(myIdentityPool.authenticatedRole);

ReactウェブサイトからAPIを呼び出すには、connectionジェネレーターを使用できます。これにより、Smithyモデルから型安全なクライアント生成が提供されます。

connectionジェネレーターを使用して、このプロジェクトをワークスペース内の他のプロジェクトと統合します。このプロジェクトに関連する次の接続があります:

Smithy
React to Smithy APIReactウェブサイトからSmithy APIを呼び出す
SmithyAmazon Aurora
Smithy API to Relational DatabaseSmithy APIをAuroraリレーショナルデータベースに接続
SmithyAmazon DynamoDB
Smithy API to TypeScript DynamoDBSmithy APIをDynamoDBテーブルに接続