はじめに
Amazon API Gatewayがストリーム応答をサポートするアップデートがありました。
いきなりですが、下はストリーミングと非ストリーミングの比較です。
めっちゃ見やすくなってますよね。
今回は動作検証をしながら、どのようなアップデートなのか紹介できればと思います。
何が変わった?
API Gateway はこれまで、バックエンドがレスポンスをすべて生成し終わってからまとめて返す “バッファ方式” が基本でした。
そのため、処理が重い API や、生成系の処理(例:Bedrock で文章を生成する API)では 最初のレスポンスが返ってくるまで待ち時間が長くなる という課題がありました。また、レスポンス全体をまとめて返す都合上、10MB のレスポンスサイズ制限 や 29 秒のタイムアウト の影響もあり、大きな出力や長時間処理には工夫が必要でした。
今回追加された「レスポンス・ストリーミング(Response Streaming)」は、バックエンドが生成したデータをそのまま 逐次クライアントへ送れるようになった新機能です。
Bedrock のような「トークン単位で徐々に出力が進む」モデルとは相性が良く、ユーザーは“最初の一文字目”をすぐに見ることができるようになります。これにより、チャットボットや文章生成系 API の体感速度(UX)は大きく改善されます。
作ってみる
本記事では、この新しいストリーミング機能を Amazon Bedrockをバックエンドに使った構成 で検証します。
- API Gateway
- Lambda
- Bedrock
という構成を用意し、 - 従来のバッファ方式とどう変わるのか
- Bedrock の出力がどのようにストリーミングされるのか
- UI 側ではどのタイミングでデータが到達するのか
を確認しながらまとめていきます。
構成図
AWSの公式ブログから構成図を拝借し、以下のような構成で今回検証します。
Lambda関数
Lambdaのストリーミング関数ですが、現在Node.jsにしか対応していないようでした😭
なので、今回はNode.jsで実施していきます。
コードは以下になります!また、Lambdaのタイムアウト時間も30秒くらいにしています。
import {
BedrockRuntimeClient,
ConverseStreamCommand,
} from "@aws-sdk/client-bedrock-runtime";
// Lambda Response Streaming
export const handler = awslambda.streamifyResponse(
async (event, responseStream, _ctx) => {
// ---------------------------
// 1. API Gateway 用のメタデータ
// ---------------------------
const httpStream = awslambda.HttpResponseStream.from(responseStream, {
statusCode: 200,
headers: {
"Content-Type": "text/plain; charset=utf-8",
"x-api-gw-streaming": "true",
},
});
try {
// ---------------------------
// 2. 入力取得(POST body)
// ---------------------------
let userPrompt = "あなたのAWSの推しサービスを教えて";
if (event?.body) {
try {
const body = JSON.parse(event.body);
userPrompt = body.message ?? body.prompt ?? userPrompt;
} catch (_) {
/* malformed JSON → デフォルトで進む */
}
}
// ---------------------------
// 3. Bedrock クライアント
// ---------------------------
const client = new BedrockRuntimeClient({
region: process.env.BEDROCK_REGION ?? "us-west-2",
});
// Claude 4.5 Haiku(Inference Profile)
const modelId =
"global.anthropic.claude-haiku-4-5-20251001-v1:0";
// ---------------------------
// 4. ConverseStream 呼び出し
// ---------------------------
const command = new ConverseStreamCommand({
modelId,
messages: [
{
role: "user",
content: [
{ type: "text", text: userPrompt },
],
},
],
});
const response = await client.send(command);
// ---------------------------
// 5. Bedrock のチャンクを逐次処理 → API Gateway へ流す
// ---------------------------
for await (const item of response.stream) {
if (!item?.contentBlockDelta) continue;
const delta = item.contentBlockDelta.delta;
const text = delta?.text;
if (text) {
httpStream.write(text);
}
}
// ---------------------------
// 6. 完了
// ---------------------------
httpStream.end();
} catch (e) {
// ---------------------------
// 7. エラー時もストリームを閉じる
// ---------------------------
console.error("Lambda Error:", e);
httpStream.write("\n[ERROR]\n");
httpStream.write(String(e));
httpStream.end();
}
}
);
Lambda関数の実行ロールにBedrockのポリシーをつけるのも忘れずに!
API Gateway
LambdaのトリガーとなるAPI Gatewayは設定欄から追加することができます。
あとは、APIをデプロイするだけです。
動作確認
以下のコマンドターミナルから実行できます。
curl --no-buffer {URL}
実行時は以下のようなイメージです!
やはり処理がストリーミングされてくるといいですね。
さいごに
今回はAPI Gateawayのストリーミング処理を試してみました。
今回はシンプルにモデル呼び出しでしたが、AgentCoreやStrandsと組み合わせるとより面白そうではありますね。
機会があれば試してみたいと思います。









