1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

3つの特徴で理解する AWS Blocks 〜IFC・Conditional Exports・CDKの拡張〜

1
Last updated at Posted at 2026-06-23

はじめに

こんにちは!CDK が好きな社会人二年目のエンジニアです。

2026 年 6 月 16 日、AWS から AWS Blocks がパブリックプレビューとして発表されました。TypeScript で書かれたオープンソースのバックエンドツールキットで、「アプリケーションコードを書くと、そこからインフラが導出される」という Infrastructure from Code(IFC) のアプローチを採用しています。

IaC の普及により、インフラをコードで管理すること自体は当たり前になりました。しかし、アプリケーションコードとインフラ定義は依然別々のファイルに分かれており、それが原因で課題を感じることも多いでしょう。

CDK なら lib/ にスタック定義、src/ にアプリロジックを定義しますが、アプリ側でカラムを 1 つ足したら、インフラ側のテーブル定義も直すことになります。
さらに、ちょっとしたロジックを試したいだけなのに、まずデプロイ環境を用意しなければ確認すらできない状況も多いです。

AWS Blocks はこの課題を、「インフラ定義を書く」という工程そのものをなくすことで解決しようとしています。

本記事では、AWS Blocks の技術的な仕組みを以下の 3 つの特徴 に整理して解説し、後半では実際に手を動かして体感するハンズオンを行います。

  1. Infrastructure from Code(IFC)
  2. Conditional Exports
  3. CDK の拡張

AWS Blocks はとても面白く、また使いこなせれば非常に便利なツールになっていますので、是非当記事で興味を持っていただけると嬉しいです!

当記事は、2026 年 6 月に執筆しております。パブリックプレビューの状態であり、今後大幅な変更が入る可能性もございますので、最新情報は公式ドキュメントをご確認ください。

AWS Blocks とは

AWS Blocks は、フルスタックアプリのバックエンドを素早く作るためのツールキットです。
以下のコードは、認証機能を備えた CRUD API を AWS Blocks で実装した例です。

import { ApiNamespace, Scope, KVStore, AuthBasic } from '@aws-blocks/blocks';

const scope = new Scope('notes-app');
const auth = new AuthBasic(scope, 'auth');
const notes = new KVStore(scope, 'notes', {});

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async create(title: string, body: string) {
    const user = await auth.getCurrentUser(context);
    const id = crypto.randomUUID();
    const note = { id, title, body, createdAt: Date.now() };
    await notes.set(`${user.userId}:${id}`, note);
    return note;
  },

  async list() {
    const user = await auth.getCurrentUser(context);
    return notes.list({ prefix: `${user.userId}:` });
  },

  async delete(id: string) {
    const user = await auth.getCurrentUser(context);
    await notes.delete(`${user.userId}:${id}`);
  },
}));

export { auth };

シンプルですが、このコードにはいくつか不思議な点があります。
CDK と異なり、インフラの定義が見当たりません。DynamoDB テーブルを作る CDK スタックも、Lambda の設定も、API Gateway のリソースも書いていないにも関わらず、このコードだけで動くアプリができあがります。

このように、インフラを直接定義せずにアプリのコードを書くだけでインフラが出来上がる、これが AWS Blocks のコアコンセプトです。

AWS Blocks で提供される、KVStoreのようなリソースを、Blockと言います。公式ドキュメントでは以下のように説明されています。

ブロックとは、クラウド リソース、ランタイム API、ローカル実装といった完全な機能を提供するモジュールです。各ブロックは npm パッケージであり、インポートしてバックエンド コードで使用します。

ここで注目したいのは、クラウド リソース、ランタイム API、ローカル実装といった完全な機能を提供するモジュールですという記述です。

Github では、以下のように説明されています。

Mock data persists to disk at .bb-data/{fullId}/ across dev server restarts. Wipe with rm -rf .bb-data. The mock validates the 400 KB item size limit, schema validation, and conditional check failures, matching AWS behavior.

つまり、KVStoreは、AWS にデプロイする際は DynamoDB がプロビジョニングされますが、ローカルでの実行時はモックデータとして、.bb-data/{fullId}/に書き込まれるということです。これにより、実際にデプロイしなくても実装したアプリケーションの検証が可能になります。

2026 年 6 月 21 日時点で用意されている Block 群

データとストレージ

Block 説明 AWSサービス
KVStore 条件付き書き込み対応のキーバリューストレージ Amazon DynamoDB
DistributedTable インデックスとクエリを備えた構造化データ Amazon DynamoDB
Database Kyselyクエリビルダーを使ったSQLデータベース(マネージドPostgres) Amazon Aurora Serverless v2
DistributedDatabase アイドルコストゼロのサーバーレスSQL Amazon Aurora DSQL
FileBucket 署名付きURLを使ったファイルストレージ Amazon S3

認証

Block 説明 AWSサービス
AuthBasic ユーザー名/パスワード認証(JWT) Amazon DynamoDB + JWT
AuthCognito MFA・グループ・パスキー対応の認証 Amazon Cognito
AuthOIDC OIDCサインイン(Google、GitHub、Okta等) OAuthリダイレクトフロー

コンピュートとバックグラウンド処理

Block 説明 AWSサービス
AsyncJob 非同期バックグラウンド処理 Amazon SQS + AWS Lambda
CronJob スケジュール実行 Amazon EventBridge + AWS Lambda

AI

Block 説明 AWSサービス
Agent ツール呼び出し・HITL承認・会話継続対応のAIエージェント Amazon Bedrock
KnowledgeBase セマンティック検索とRAG Amazon Bedrock Knowledge Bases

コミュニケーション

Block 説明 AWSサービス
Realtime WebSocket pub/subチャネル Amazon API Gateway WebSocket
EmailClient トランザクションメール送信 Amazon SES

設定

Block 説明 AWSサービス
AppSetting 設定値とシークレット管理 AWS Systems Manager Parameter Store

オブザーバビリティ

Block 説明 AWSサービス
Logger 相関ID付き構造化ログ Amazon CloudWatch Logs
Metrics カスタムアプリケーションメトリクス Amazon CloudWatch
Tracer 分散トレーシング AWS X-Ray
Dashboard 自動生成オブザーバビリティダッシュボード Amazon CloudWatch

ホスティング

Block 説明 AWSサービス
Hosting SSR対応フロントエンドデプロイ Amazon CloudFront + Amazon S3

このように、フルスタックアプリのバックエンドを素早く作るためのツールキットとして多様な機能を備えた AWS Blocks ですが、前述した通り以下の3つの特徴に大きくまとめることができます。

  1. Infrastructure from Code(IFC)
    アプリのコードを書くと、そのコードからインフラが導出される
  2. Conditional Exports
    実行環境により、同じimportが別ファイルに解釈される
  3. CDK の拡張
    CDK レイヤーの上で動作し、CDK を直接触ることもできる

以下それぞれ見ていきます。

Infrastructure from Code(IFC)

IFCとは

Infrastructure as Code(IaC)ではなく、Infrastructure from Code(IFC)です。
from とある通り、アプリのコードからインフラを導出します。

IFC 自体は、AWS Blocks に特有の概念ではありません。既存のツールとしては、Encore などが有名です。

従来の IaC(Infrastructure as Code)では、アプリケーションコードとインフラ定義は別々に書きます。
CDK なら lib/ にスタック定義、src/ にアプリロジックを書く場合が多いでしょう。
しかし、AWS Blocks の IFC(Infrastructure from Code)はアプリケーションコードを書くと、そこからインフラが導出されます。

// aws-blocks/index.ts - the IFC layer
import { ApiNamespace, Scope, KVStore, AuthBasic } from '@aws-blocks/blocks';

const scope = new Scope('my-app');

// ユーザーレコード用にDynamoDBテーブルをプロビジョニングし、セッション管理用にJWTが発行される
const auth = new AuthBasic(scope, 'auth');
const todos = new KVStore(scope, 'todos', {});

// API ロジックも同一レイヤーに書く
export const api = new ApiNamespace(scope, 'api', (context) => ({
  async createTodo(title: string) {
    const user = await auth.getCurrentUser(context);
    await todos.set(`${user.userId}:${title}`, { title, done: false });
  },
}));

export { auth };

このように、開発者は「データベースを使う」「認証を使う」というアプリのロジックを書いているだけで、裏側のインフラは AWS Blocks が導出してくれます。
これは、開発者にとって、機能を定義するだけで、インフラができるということを意味します。
従来であれば、アプリケーションを実装するにあたって、DynamoDB、IAM、Lambda など AWS の知識が必要ですし、AWS におけるベストプラクティスを学び、それに沿って開発する必要があります。
CDK を用いて開発する際にも、抽象化されてはいますが CDK の書き方などのアプリケーションレイヤー以外の学習コストがかかります。

一方 AWS Blocks は、開発者はアプリケーションを意識して IFC レイヤーにて機能単位で実装するだけで、たとえ AWS を知らなくても AWS のベストプラクティスに沿ってインフラが構築されます。
これが、Infrastructure from Code(IFC)の強みだといえます。

裏側で何が起こっているのか

IFC レイヤーでnew KVStore(scope, 'todos', {})すると何が起こるかを知るために、CDK synth をした時にたどり着く実装を見てみます。

以下がコードの抜粋ですが、このように DynamoDB テーブルの定義から Lambda への IAM 権限付与まで、Block のコンストラクタが全部やってくれています。

ts index.cdk.ts
// packages/bb-kv-store/src/index.cdk.ts(抜粋)
constructor(scope: ScopeParent, id: string, options?: KVStoreOptions<unknown>) {
  super(id, { parent: scope });
  this.table = new Table(this, 'table', {
    partitionKey: { name: 'pk', type: AttributeType.STRING },
    billingMode: BillingMode.PAY_PER_REQUEST,
  });
  // Lambda への IAM 権限付与も自動でやってくれる
  this.table.grantReadWriteData(this.handler);
}

上記のファイルはローカル検証時にたどり着くファイルではなく、AWS デプロイ時にのみたどり着くファイルですが、機能単位での定義でインフラがプロビジョニングされるという IFC の仕組みを理解することができます。

Conditional Exports

Conditional Exports とは

前のセクションで、IFC レイヤーに new KVStore(scope, 'todos', {}) と書くだけでインフラが導出されることを確認しました。
ただし、常に AWS リソースが導出されるだけでなく、同じ new KVStore(...) という 1 行が、ローカル開発ではファイルシステムへのモック書き込みになります。これが AWS Blocks の大きな特徴であり、利点です。

このコードを一切変えていないのに、実行するコンテキストによって振る舞いが変わる仕組みを実現しているのが、Node.js の Conditional Exports です。

Conditional Exports は、package.jsonexports フィールドで、同じ import パスを実行条件(condition)ごとに異なるファイルに解決できる Node.js の標準機能です。

開発者が書く import 文はいつも同じですが、この 1 行が実際にどのファイルに解決されるかは、実行時の condition によって変わります。

import { KVStore } from '@aws-blocks/blocks';
実行コンテキスト condition 解決先 振る舞い
ローカル開発(npm run dev default index.mock.js インメモリ/ファイルシステムのモック実装
CDK synth(npm run deploy の裏側) cdk index.cdk.js CDK コンストラクト(インフラ定義)
Lambda 実行時 aws-runtime index.aws.js AWS SDK 呼び出し(本番ランタイム)

実際の開発フローでは、開発者が condition を手で指定する必要はありません。AWS Blocks が提供するコマンドが、裏側で適切な condition を設定してくれます。

コマンド 用途
npm run dev ローカル開発サーバー起動。AWS アカウント不要
npm run sandbox バックエンドだけ AWS にデプロイ(Lambda ホットスワップで高速)。フロントはローカルのまま
npm run deploy 本番デプロイ。フル CDK デプロイ(CloudFormation スタック作成)
npm run destroy 本番環境の削除
npm run sandbox:destroy サンドボックス環境の削除

npm run dev でローカル検証し、npm run sandbox で実際の AWS サービスに繋いで確認し、npm run deploy で本番に出すという一連の流れの中で、開発者が書くアプリケーションコードは一切変わらないということです。

裏側で何が起こっているのか

では、実際の Block パッケージの package.json を確認してみます。
以下は bb-kv-store パッケージexports フィールドの構造です。

{
  "exports": {
    ".": {
      "browser": "./dist/index.browser.js",
      "cdk": {
        "types": "./dist/index.cdk.d.ts",
        "default": "./dist/index.cdk.js"
      },
      "aws-runtime": "./dist/index.aws.js",
      "types": "./dist/index.mock.d.ts",
      "default": "./dist/index.mock.js"
    }
  }
}

Node.js の Conditional Exports では、リゾルバが上から順に condition をマッチさせ、最初に一致したものを使います。default は必ず最後に置き、どの condition にもマッチしなかった場合のフォールバックとして機能します。

開発者は import { KVStore } from '@aws-blocks/blocks' と書くだけで、あとは AWS Blocks と Node.js の仕組みが適切な実装を選んでくれます。

CDK の拡張

なぜ CDK を拡張できることが重要なのか

ここまで見てきたように、AWS Blocks は IFC と Conditional Exports の仕組みにより、開発者がインフラを意識せずにアプリケーションを構築できるようになっています。
アプリコードを書くだけでインフラが導出される、これは非常に便利ですが、IFC というアプローチはインフラ定義の柔軟性にかけることが多く、以前からその課題が指摘されてきました。

では、AWS Blocks はこうした課題にどう対応しているのでしょうか。

公式ドキュメントでは以下のように説明されています。

No ceiling on what you can build
Every Block uses production AWS services. When you need more control, drop into CDK and configure resources directly. You're never stuck in an abstraction.

つまり、AWS Blocks では Block として用意されていないリソースが必要になったら CDK コンストラクトを直接使うことができ、かつ既存の AWS リソースを Block でラップすることもできるということです。
これは AWS が公式に提供している IFC ツールである大きな強みと言えます。

AWS Blocks のレイヤー構造

AWS Blocks のプロジェクトには、役割の異なる 2 つのエントリポイントがあります。

ファイル レイヤー 役割
aws-blocks/index.ts IFC レイヤー アプリケーションロジック。Block を使って機能を定義する
aws-blocks/index.cdk.ts CDK レイヤー インフラ定義。CDK コンストラクトを直接使う(オプショナル)

これまでのセクションで扱ってきたコードは、すべて IFC レイヤー(index.ts)のものでした。通常の開発では AWS Blocks が IFC レイヤーからデフォルトの CDK アプリケーションを自動生成してくれるため、IFC レイヤーだけで完結します。
Block として用意されていないリソースの追加や、既存 CDK スタックとの統合など、より細かなインフラ制御が必要になったとき、CDK レイヤーに「降りる」ことができます。

CDK レイヤーの使い方

AWS Blocks で CDK を直接触るには、aws-blocks/index.cdk.ts というファイルを使います。IFC レイヤー(aws-blocks/index.ts)はアプリケーションロジックを書く場所ですが、CDK レイヤー(aws-blocks/index.cdk.ts)はインフラを直接定義する場所です。

公式ドキュメントの例を見てみます。SQS キューを追加するケースです。

// aws-blocks/index.cdk.ts(CDK レイヤー)
import * as cdk from 'aws-cdk-lib';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { BlocksStack } from '@aws-blocks/blocks/cdk';

const app = new cdk.App();
const stack = await BlocksStack.create(app, 'my-app', {
  backendHandlerPath: './index.handler.ts',
  backendCDKPath: './index.ts',
});

// SQS キューを CDK コンストラクトとして直接定義
const queue = new sqs.Queue(stack, 'work-queue');

// stack.handler は AWS Blocks が管理する Lambda(NodejsFunction)
queue.grantSendMessages(stack.handler);
stack.handler.addEnvironment('QUEUE_URL', queue.queueUrl);
// aws-blocks/index.ts(IFC レイヤー)
import { Scope, ApiNamespace } from '@aws-blocks/blocks';
import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';

const scope = new Scope('my-app');
const sqs = new SQSClient({});

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async enqueue(payload: Record<string, unknown>) {
    await sqs.send(new SendMessageCommand({
      QueueUrl: process.env.QUEUE_URL!,
      MessageBody: JSON.stringify(payload),
    }));
    return { ok: true };
  },
}));

stack.handler は AWS Blocks が内部で管理している Lambda 関数(NodejsFunction)への参照で、IFC レイヤーの全メソッドが同居する Lambda-lith 構成のエントリポイントになります。NodejsFunctionなので、 grants メソッドや addEnvironment をそのまま使うことができます。

なお、既存の CDK スタックに AWS Blocks を組み込みたい場合は、BlocksStack の代わりに BlocksBackend を使います。BlocksBackend は CDK Construct として既存スタック内に配置でき、同様に .handler を通じてリソースを接続できます。

fromExisting による既存リソースの利用

新しいリソースを作るだけでなく、既にある AWS リソースを Block でラップすることもできます。たとえば、既存の DynamoDB テーブルを AWS Blocks から使いたい場合は、fromExisting を使います。

// aws-blocks/index.ts(IFC レイヤー)
import { Scope, KVStore, ApiNamespace } from '@aws-blocks/blocks';

const scope = new Scope('my-app');

const sessions = new KVStore(scope, 'sessions', {
  table: KVStore.fromExisting('my-legacy-sessions-table'),
});

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async getSession(token: string) {
    return sessions.get(token);
  },
}));

fromExisting を使うと、Block は新しいリソースをプロビジョニングする代わりに、指定した既存リソースに接続します。npm run dev でのローカル開発時はインメモリのモック実装が使われるので、ローカルサポートも維持されます。
KVStore だけでなく、DistributedTableFileBucketDatabaseAuthCognito など複数の Block が fromExisting に対応しており、既存のインフラを持つプロジェクトでも段階的に AWS Blocks を導入していくことが可能です。

実際に動かしてみる

テンプレートにはデフォルトで TODO アプリが実装されており、それだけでも AWS Blocks のコアコンセプトは体感できますが、今回は1から認証付きのカレンダーアプリを実装してみます。

環境構築

詳細は公式の Getting Started を参照しつつ、実際の手順や生成されるファイルを見ていきます。

必要なもの

ローカルで動かすだけであれば、以下だけで十分です。

  • Node.js 22 以上
  • npm 10 以上(Node.js に同梱されているので別途インストール不要)
  • TypeScript 対応のエディタ(VS Code、Kiro など)

AWS へのデプロイまで行う場合は、追加で以下が必要です。

  • AWS CLI
  • CDK Bootstrap 済みの AWS アカウント

デプロイについては後述しますが、ローカル実行だけなら AWS アカウントは不要というのが AWS Blocks の大きな魅力です。

プロジェクトの作成

npm create @aws-blocks/blocks-app@latest test-app
cd test-app
npm install

作成されるディレクトリ構造はシンプルです。

my-todo-app/
├── aws-blocks/
│   └── index.ts     # IFC レイヤー(バックエンドロジックはここに書く)
├── src/
│   └── app.tsx      # フロントエンド
└── package.json

また、初期のテンプレートに AGENTS.md が含まれています。

# Agent Guide

## Quick Reference

- **Backend:** `aws-blocks/index.ts` — APIs, auth, data models
- **Frontend:** `src/` — imports backend APIs via `import { api } from 'aws-blocks'`
- **Tests:** `test/e2e.test.ts` — run with `npm run test:e2e`
- **Full guide:** `node_modules/@aws-blocks/blocks/README.md`
- **Block catalog + decision tree:** `node_modules/@aws-blocks/blocks/docs/index.md`
- **Per-block docs:** `node_modules/@aws-blocks/blocks/docs/<package-name>.md`
...

さらに注目したいのが、AGENTS.md に記載されているドキュメントの参照先です。

- **Full guide:** `node_modules/@aws-blocks/blocks/README.md`
- **Block catalog + decision tree:** `node_modules/@aws-blocks/blocks/docs/index.md`
- **Per-block docs:** `node_modules/@aws-blocks/blocks/docs/<package-name>.md`

ドキュメントが node_modules の中にバンドルされています。AI エージェントが別途最新情報を取ってこなくても、ローカルのファイルシステムを参照するだけで各 Block の仕様を把握できるように設計されています。
これは実装する上で非常に嬉しいポイントです。

IFC を体感する

バックエンド実装

では、実際に認証付きのカレンダーアプリを実装してきます。
バックエンドはすべて、IFCレイヤーである aws-blocks/index.ts に書きます。

// aws-blocks/index.ts
import { ApiNamespace, Scope, AuthBasic, DistributedTable } from '@aws-blocks/blocks';
import { z } from 'zod';

const scope = new Scope('my-app');

// → DynamoDB テーブル(ユーザー管理)と JWT 認証が導出される
const auth = new AuthBasic(scope, 'auth', { passwordPolicy: { minLength: 8 } });
export const authApi = auth.createApi();

const eventSchema = z.object({
  userId: z.string(),
  eventId: z.string(),
  title: z.string(),
  date: z.string(),           // 'YYYY-MM-DD'
  startTime: z.string().optional(),
  description: z.string().optional(),
  color: z.string().optional(),
  version: z.number(),
  createdAt: z.number(),
});

// → インデックス付き DynamoDB テーブルが導出される
const events = new DistributedTable(scope, 'events', {
  schema: eventSchema,
  key: { partitionKey: 'userId', sortKey: 'eventId' },
  indexes: {
    byDate: { partitionKey: 'userId', sortKey: 'date' },
  },
});

// → API Gateway + Lambda が導出される
export const api = new ApiNamespace(scope, 'api', (context) => ({
  async createEvent(input: {
    title: string;
    date: string;
    startTime?: string;
    description?: string;
    color?: string;
  }) {
    const user = await auth.requireAuth(context);
    const eventId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
    const event = {
      userId: user.username,
      eventId,
      ...input,
      version: 1,
      createdAt: Date.now(),
    };
    await events.put(event);
    return event;
  },

  async listEventsByMonth(year: number, month: number) {
    const user = await auth.requireAuth(context);
    const ym = `${year}-${String(month).padStart(2, '0')}`;
    return Array.fromAsync(
      events.query({
        index: 'byDate',
        where: {
          userId: { equals: user.username },
          date: { between: [`${ym}-01`, `${ym}-31`] },
        },
      })
    );
  },

  async deleteEvent(eventId: string) {
    const user = await auth.requireAuth(context);
    await events.delete({ userId: user.username, eventId });
    return { success: true };
  },
}));

インフラ定義を書かずに、アプリのロジックを書いているだけで実装が完結しているのがわかります。

フロントとバックエンド間の型共有

IFC レイヤーに定義した api を、フロントはそのまま import して呼べます。

// src/calendar/CalendarApp.tsx
import { api } from '../api'; // aws-blocks から re-export されている

// api.* はすべて型が効く
const data = await api.listEventsByMonth(year, month);

OpenAPI からのコード生成も、クライアント SDK の初期化も、fetch ラッパーも不要で、api. と打てば IDE による型補完が効きます。

試しに listEventsByMonth の引数を修正してみます。

// aws-blocks/index.ts — 引数を変更
async listEventsByMonth(ym: string) { ... }
//                     ^^^^^^^^^
//                     year, month の 2 引数 → ym: string の 1 引数に

すると、フロント側がコンパイルエラーになります。

// src/calendar/CalendarApp.tsx
const data = await api.listEventsByMonth(year, month);
//                                       ^^^^^^^^^^^

バックエンドのインターフェース変更が即座に、フロントエンドに反映されていることがわかります。

JSON-RPC 2.0

API周りの裏側を少し触れておきます。

ApiNamespace のメソッドは、デプロイすると /aws-blocks/api への POST エンドポイントとして公開されます。プロトコルは JSON-RPC 2.0 です。

フロントから呼んだ api.listEventsByMonth(2026, 6) は、裏側で以下のようなリクエストになっています。

curl -X POST http://localhost:3001/aws-blocks/api \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"api.listEventsByMonth","params":[2026,6],"id":1}'
# → {"jsonrpc":"2.0","result":[...],"id":1}

method<エクスポート変数名>.<メソッド名> の形式です。aws-blocks/index.tsexport const api = new ApiNamespace(...) としているので api.listEventsByMonthexport const authApi = auth.createApi() としているので authApi.signIn というメソッド名になります。

ただし、フロントからこのペイロードを自分で組み立てる必要はなくimport { api } from 'aws-blocks' して呼ぶだけで、フレームワークが透過的に JSON-RPC へ変換してくれます。

AWS Blocks を使用することで、API 周りの実装はかなり楽になりますね。

API の認証

ApiNamespace に定義したメソッドは、デフォルトでは誰でも呼べる公開エンドポイントになるため、認証が必要なメソッドには、別途 auth.requireAuth(context) を呼ぶことでゲートをかけます。

export const api = new ApiNamespace(scope, 'api', (context) => ({
  // 認証なし — 誰でも呼べる
  async ping() { return 'pong'; },

  // 認証あり — requireAuth が通らないと処理が進まない
  async createEvent(input: { title: string; date: string }) {
    const user = await auth.requireAuth(context); 
    // ...
  },
}));

意図しない公開エンドポイントを作らないよう、この点は注意が必要です。

Conditional Exports による実行環境の切り替えを体感する

実装したカレンダーアプリを、AWS アカウントなしでローカルで動かしてみます。

npm run dev

npm run dev

これだけで、バックエンドの API サーバーとフロントエンドの Vite 開発サーバーの両方が立ち上がります。

ブラウザで http://localhost:3000 を開くと、ユーザー登録 → ログイン → カレンダー操作まで一通り動きます。

実際にユーザー登録を行うと、設定した以外のパスワードでは認証が通らないことを確認できます。
image.png

また、イベントの登録も完了でき、他ユーザーにはこのイベントが表示されません。一時的にプロセスを落としてもデータは保持され、永続化されています。
image.png

.bb-data

プロジェクトルートに生成される .bb-data ディレクトリを覗いてみます。

.bb-data/
├── my-app-auth-users/
│   └── store.json       # 登録ユーザーのパスワードハッシュ
├── my-app-auth-codes/   # メール確認コードなど
├── my-app-events/
│   └── data.json        # 作成したカレンダーイベント
└── settings.json        # JWT シークレット

ユーザーを登録してイベントを作成すると、data.json はこのようになっています。

// .bb-data/my-app-events/data.json
[
 [
    "[\"test\",\"mqo1nmn9-5qkosj\"]",
    {
      "userId": "test",
      "eventId": "mqo1nmn9-5qkosj",
      "title": "テスト",
      "date": "2026-06-21",
      "color": "#222222",
      "version": 1,
      "createdAt": 1782061921125
    }
  ]

.bb-data ディレクトリに、データが格納されていることが確認できます。

npm run sandbox

ローカルで動作確認ができたので、次は実際の AWS サービスに繋いで確認してみます。

npm run sandbox

npm run sandbox は、バックエンド(Lambda + API Gateway + DynamoDB)だけを AWS にデプロイし、フロントエンドは引き続きローカルの Vite 開発サーバーで動かします。

CDK レイヤー(aws-blocks/index.cdk.ts)の実装をみると、以下の条件分岐が書かれています。

if (sandboxMode) {
  // スタック内の全リソースを DESTROY ポリシーに設定。
  // npm run sandbox:destroy で全て消せるようにする。
  RemovalPolicies.of(blocksStack).destroy();
  Mixins.of(blocksStack).apply(new SandboxDisableDeletionProtection());

  // フロントは localhost、API は API Gateway — クロスドメインになるため
  // Cookie に SameSite=None; Secure などのクロスドメイン属性を付与する。
  blocksStack.handler.addEnvironment('BLOCKS_SANDBOX', 'true');
}

サンドボックス環境として、RemovalPolicyが設定されていること、クロスドメイン対応が行われていることが確認できます。

試しにイベントを登録してみたところ、フロントエンドはlocalhostで動いていますが、作成された DynamoDB に実際のデータが格納されていました。
image.png

npm run deploy

npm run deploy

本番デプロイでは、バックエンドに加えてフロントエンドのホスティングも AWS 上に構築されます。

// サンドボックスでない場合のみ静的サイトホスティングを追加
if (!sandboxMode) {
  new Hosting(blocksStack, 'Hosting', {
    root: join(__dirname, '..'),
    buildCommand: 'npm run build',
    buildOutputDir: 'dist',
    api: blocksStack
  });
}

Hosting Block が npm run build を実行して dist/ をビルドし、S3 + CloudFront でホスティングされ、実際に AWS 環境で動作することを確認できます。デフォルトでは CloudFront のデフォルトドメインになりますが、カスタムドメインでのホスティングにも対応しています。

CDK を拡張する

最後に、デフォルトの Block で用意されていないリソースを実際に追加してみます。

// aws-blocks/index.cdk.ts
import * as cdk from 'aws-cdk-lib';
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';

// Lambda のエラーアラーム
new cloudwatch.Alarm(blocksStack, 'HandlerErrorAlarm', {
  metric: blocksStack.handler.metricErrors({ period: cdk.Duration.minutes(5) }),
  threshold: 1,
  evaluationPeriods: 1,
  alarmDescription: 'Lambda handler error detected',
});

実装したカレンダーアプリに CloudWatch アラームを追加してみました。
blocksStack.handler は AWS Blocks が管理する Lambda 関数への参照で、通常の CDK の NodejsFunction と同じなので、metricErrors() などの標準メソッドがそのまま使えます。
実際に、CloudWatch アラームがデプロイできていることを確認できました。
image.png

抽象度が高い Block ですが、CDK レイヤーを触れ自由にリソースを追加できるのはとても嬉しい機能です。

まとめ

本記事では、AWS Blocks を以下の 3 つの特徴に整理して解説してみました。

  1. Infrastructure from Code(IFC)
    アプリのコードを書くと、そのコードからインフラが導出される
  2. Conditional Exports
    実行環境により、同じimportが別ファイルに解釈される
  3. CDK の拡張
    CDK レイヤーの上で動作し、CDK を直接触ることもできる

パブリックプレビューということもあり、まだ複雑な要件を持つ実際のプロダクトには使えないかもしれませんが、簡易的な Web アプリであれば爆速で開発、検証ができる、とても便利で面白いツールであると感じました。
今後 AWS Blocks がより進化し、様々なリソースやユースケースに対応して行くことが今から楽しみです。

最後に、ここまで読んでくださった皆様、誠にありがとうございました!

参考文献

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?