メインコンテンツまでスキップ

AWS サーバーレスでブログのコメント API を自作した

このブログにコメント機能を追加したくなり、AWS サーバーレスで API を自作しました。その設計と実装について紹介します。

構成

リクエストは以下の流れで処理されます。

ブラウザ (www.hikari-dev.com)
↓ HTTPS
API Gateway
├── GET /comment?postId=... → コメント取得
├── POST /comment → コメント投稿
└── PATCH /comment/{id} → 管理(非表示切り替え)

Lambda (Node.js 20 / arm64)

DynamoDB(コメント保存)
+ SES v2(管理者へのメール通知)

コードは TypeScript で書き、SAM(Serverless Application Model)で IaC 管理しています。Lambda は arm64(Graviton2)にして少しコストを抑えています。

DynamoDB のテーブル設計

テーブル名は blog-comments、パーティションキーは postId、ソートキーは commentId です。

キー説明
postIdString記事の識別子(例: /blog/2026/03/20/hime
commentIdStringULID(時系列ソート可能な ID)

ソートキーに ULID を使っているので、QueryCommand で取得したコメントは投稿順に自動的に並びます。UUID にしなかったのはこの理由です。

スパムフィルタリング

コメントを DynamoDB に書き込む前に、keywords.json に定義したキーワードと照合します。

キーワードにヒットした場合は isHidden: true で自動非表示にし、isFlagged: "1" を付与します。ヒットしなければ即時公開です。

isFlagged は Sparse GSI のキーとして使っています。ヒットしないコメントにはこの属性を持たせないため、GSI に余計なパーティションが増えず、コストと効率の両面で有利です。DynamoDB Document Client の removeUndefinedValues: true を設定するだけで実現できます。

管理者へのメール通知

コメントが投稿されるたびに SES v2 で自分宛にメールが届きます。本文には投稿者名、本文、評価、IP アドレス、フラグ状態が含まれます。

メール送信は非同期で行い、失敗しても握りつぶします。コメント投稿のレスポンス速度に影響しないようにするためです。

プライバシーへの配慮

DynamoDB には IP アドレスや User-Agent も保存しますが、GET エンドポイントのレスポンスには含めません。型定義レベルで分離しています。

セキュリティ

対策
ネットワークAWS WAF で 100 req / 5 分 / IP のレート制限
CORShttps://www.hikari-dev.com のみ許可
管理 APIAPI Gateway の API キー認証(X-Api-Key ヘッダー)
スパムキーワードフィルタで自動非表示

管理エンドポイント(PATCH /comment/{id})は SAM テンプレートで ApiKeyRequired: true を設定するだけで API キー認証が有効になります。Lambda Authorizer を自前で実装する必要がなく、シンプルです。

まとめ

サーバーレス構成なのでサーバー管理も不要で、DynamoDB のオンデマンド課金により低トラフィックな個人ブログでもコストを最小限に抑えられています。

コードは SAM + TypeScript + esbuild でまとめており、sam build && sam deploy だけでデプロイできます。

複数の生成 AI エージェントを利用可能なチャットの VSCode 拡張機能 Hime を作成

複数の AI プロバイダーとチャットできる VSCode 拡張機能 Hime (HikariMessage) を作りました。

BYOK であり、各 API プロバイダーの API キーがあれば利用可能です。

Hime とは

Hime は、VSCode のサイドバーに組み込む生成 AI チャット拡張機能です。Anthropic、OpenAI、Azure OpenAI、OpenRouter、Ollama に対応しており、ドロップダウンメニューで簡単にプロバイダーを切り替えられます。

主な機能

複数の AI プロバイダーに対応

以下のプロバイダーをサポートしています。

  • Anthropic (Claude)
  • OpenAI
  • Azure OpenAI
  • OpenRouter
  • Ollama

ストリーミング表示

リアルタイムで応答が表示されるため、長い回答でも待ち時間を感じにくくなっています。

MCP

設定で以下のように JSON 形式で設定を行うことで MCP が利用可能です。

{
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Users"]
}
}

リッチな UI

  • Markdown レンダリング
  • コードブロックのシンタックスハイライト
  • コードブロックのコピーボタン
  • MCP の出力結果表示

チャット履歴の永続化

会話履歴は ~/.hime/chats/ に JSON 形式で保存されます。VSCode を再起動しても続きから会話できます。

自動システムプロンプト

ワークスペースの情報、OS の情報、現在開いているエディタのコンテキストが自動的にシステムプロンプトに含まれます。「このファイルを直して」と言うだけで、どのファイルを見ているかを AI が把握してくれます。

セットアップ

Node.js 20 以上と VSCode 1.96 以上が必要です。

git clone https://github.com/Himeyama/hime
cd hime
npm install
npm run watch # 開発時: Extension Host と Webview を同時監視

その後、VSCode で F5 を押すと拡張機能ホストが起動します。API キーはサイドバーの設定パネルから入力でき、VSCode の SecretStorage で暗号化されて保存されます。

まとめ

エディタを離れずに AI と対話でき、しかも MCP でツール実行まで任せられるのが Hime の強みです。ぜひ試してみてください。

リポジトリ: https://github.com/Himeyama/hime

AWS サーバーレスでクラウドストレージを作った

はじめに

自分専用のファイル共有システムが欲しいと思い、AWS のサーバーレスサービスだけでファイルストレージサービスを作りました。

この記事では、設計で意識したポイントと、実際のアーキテクチャを紹介します。

何を作ったか

制作した Web システムは、Web ブラウザからファイルのアップロード・ダウンロード・フォルダ管理ができるクラウドストレージサービスです。

主な機能

  • ファイルのアップロード / ダウンロード
  • フォルダの作成・階層管理
  • 複数ファイル / フォルダの一括 ZIP ダウンロード
  • ユーザー認証(サインアップ・ログイン・パスワードリセット)
  • ユーザープロフィール管理

アーキテクチャ

以下、構成図です。

認証の大部分は Cognito で行い、 ファイル転送は S3 の Presigned URL を Lambda で発行してクライアントと S3 が直接やり取りする仕組みです。

技術スタック

レイヤー技術
バックエンドC# (.NET 8) / AWS Lambda
認証Amazon Cognito + Managed Login v2
APIAPI Gateway (REST) + Cognito Authorizer
ストレージAmazon S3

設計判断とその理由

認証を Cognito で行う

Cognito の OAuth 2.0 エンドポイントと Managed Login を活用し、認証機能を実現しました。

最終的に認証系の Lambda は TokenFunction 1 つだけになりました。

機能的にもセキュリティ的にも減らせるコードは減らすのが吉です。

AWS のサービスがやってくれることを自前で書く必要はありません。

Presigned URL によるファイル転送

ファイルのアップロード・ダウンロードで Lambda を経由すると、いくつかの問題が生じます:

  • Lambda のペイロード上限に引っかかる
  • 大きなファイルを Lambda のメモリに載せるとコストがかかる
  • 転送時間が Lambda の実行時間としてカウントされる

Presigned URL なら、Lambda は URL を発行するだけで、実際のファイル転送はブラウザと S3 が直接行います。

Lambda の実行時間は数十ミリ秒で済み、ファイルサイズの制約も S3 の上限までとなります。

アップロードの流れ:
1. ブラウザ → Lambda: 「file.pdf をアップロードしたい!アップロード先の URL を送れ~」
2. Lambda → ブラウザ: 「アップロード先の Presigned URL だよ。ここに PUT してね~」
3. ブラウザ → S3: 「S3 に PUT するよ~」
4. ブラウザ → Lambda: 「アップロード完了したよ~」

4. フォルダの ZIP ダウンロード

S3 にはフォルダごとダウンロードする機能がありません。

複数ファイルの一括ダウンロードは、Lambda 上で ZIP を生成して一時的に S3 に置き、その Presigned URL を返す方式にしました。

一時 ZIP ファイルは S3 のライフサイクルルールで 1 日後に自動削除されるので、ゴミが溜まることはありません。

セキュリティ

対策実装
ブルートフォース防止Cognito 標準のロック機能 (5 回失敗: 15 分ロック)
API 保護Cognito Authorizer による JWT 検証
CORSAllowedOrigin を特定のドメインに限定
一時ファイル管理S3 ライフサイクルで不要なファイルを 1 日で自動削除

コスト

サーバーレス構成なので、利用がなければコストはほぼゼロです。

  • Cognito: ESSENTIALS Tier は MAU 10,000 まで無料
  • Lambda: 月 100 万リクエストまで無料
  • S3: 保存量に応じた従量課金(GB あたり約 $0.025/月)
  • API Gateway: 100 万リクエストあたり $3.50

個人利用なら月額数十円〜数百円程度に収まります。

インフラのコード化

インフラ全体を 1 つの template.yaml (AWS SAM) で定義しています。

Cognito User Pool、API Gateway、Lambda 3 関数、S3 バケット、CloudWatch アラーム、SNS — すべてのリソースを 600 行程度の YAML で定義しています。

WSL2 に Rocky Linux 8.10 をインストールする

WSL 2 イメージのダウンロード

https://dl.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-Container-Base.latest.x86_64.tar.xz

ここからダウンロードする。

参考: https://docs.rockylinux.org/8/guides/interoperability/import_rocky_to_wsl/

イメージを展開

tar.xz を展開し、tar 形式にする。 tar にすることで、WSL2 へのインポートが可能となる。

cd ~/Downloads

xz -d Rocky-8-Container-Base.latest.x86_64.tar.xz

※Windows にインストール済みの bsdtar では展開不可。 ※xz コマンドがない場合は、cygwin64 でインストールするか、別の WSL ディストリビューションを用いて展開する。

イメージのインポート

WSL2 にイメージをインポートする。

wsl --import RockyLinux-8.10 $HOME .\Rocky-8-Container-Base.latest.x86_64.tar --version 2

イメージの確認

wsl -l -v

ユーザーの追加とデフォルトユーザーの設定

ユーザー名: hikari の場合

wsl -d RockyLinux-8.10 -u root -- dnf install sudo passwd -y

wsl -d RockyLinux-8.10 -u root -- adduser hikari

wsl -d RockyLinux-8.10 -u root -- passwd -d hikari

wsl -d RockyLinux-8.10 -u root -- usermod -aG wheel hikari

wsl -d RockyLinux-8.10 -u root -- sed -i 's/^# %wheel/%wheel/' /etc/sudoers

wsl -d RockyLinux-8.10 -u root -- echo -e "[user]\\ndefault=hikari" `| tee -a /etc/wsl.conf

イメージの起動

wsl -d RockyLinux-8.10