⛓️

Next.js + AWS CDK / LambdaでWeb3ギャラリーというWebアプリを個人開発で作りましたとさ

に公開

はじめに

こんにちは!いけふくろうです。

web3関連の最新情報、交流、学びを提供するアプリを作りたいと思い、個人開発を2025年4月頃からスタートしました。途中何度もストップ→再開を繰り返したり、ここ数ヶ月は何もできていなかったのですが、11月中旬から再開し、一気にリリースまで対応しました!

フェーズ1の完成品

まだまだ微々たるコンテンツしかありませんが、ユーザー登録不要でユーザー参加型の投票機能などのコンテンツも入れています。
ぜひ、アクセスお願いします🙏

https://web3-gallery.net/

環境

Web

共通

  • Node.js 20.x系
  • Next.js 15.3.1

フロントエンド

  • React 19
  • CSS Modules
    • Next.jsに標準で組み込まれているスタイル定義

バックエンド

  • InversifyJS 7.5.1(DIライブラリ)
  • Prisma 6.8.2(ORMライブラリ)

データベース

  • PostgreSQL

Batch

  • AWS CDK
  • AWS Lambda
  • Node.js 20.x系

その他

  • microCMS

技術スタック

インフラ構成

本来は、Next.js(FE、BE)をAmplifyでホスティングさせる構想でしたが、VPC内リソース内に直接アクセスすることができないことを知り、仲介役としてLamdaを挟むといった構成を組まなければ難しいということを知り、Fargate/ECSによるコンテナ起動にしました。

お察しの通り、ドメインも購入しました!!

ミニマムにはしていますが、Web3 Galleryには現状収益化の仕組みは存在しないため、赤字です!!助けてください!!(笑)

項目 利用サービス
サービス構成 ECS、RDS、ALB、Route 53
サービス間の接続 ALB → ECS → RDS
リージョン ap-northeast-1

ECSはNext.jsのUI&APIコンテナとPrismaマイグレーション用コンテナの2つを配置しています。
GitHub ActionsによるCDの仕組みも入れています。

機能一覧

Web(Next.js App Router)

バックエンドはRoute Handlersを活用して、APIを提供している

  • ニュース記事

    • 外部メディア(Cointelegraph、Ethereum Blog、PR Times)から取得した最新情報を提供
  • 暗号資産マーケット情報

    • リアルタイム価格表示(CoinGecko APIを利用)
      • 主要暗号資産であるBitcoin、Ethereumの価格
  • 投票機能

    • ユーザー参加型のWeb3に関する投票機能(毎週実施)
    • 投票結果の表示
    • 投票シェア機能
  • コミュニティ機能

    • Xの#Web3Galleryハッシュタグポストの表示
    • Xへのシェア機能
  • 学習コンテンツ

    • Web3関連のYouTube動画(筆者作)埋め込み

Batch(AWS Lambda + EventBridge)

  • データ収集機能

    • RSS収集(既述の外部メディア)
  • Xポスト収集機能

    • ハッシュタグ検索(#Web3Gallery, #Web3ギャラリー

機能概要

ニュース記事

現状は3つのメディアから下記の時間帯でスケジューリングの上、Lambdaを起動し、microCMSへ登録しています。今後はCoinDeskなどからの収集を増やしていこうと考えています。
※本来は、2h刻みなどで実行が望ましいですが、個人開発事情を鑑みて...

schedule: events.Schedule.cron({
  // UTC: 22:30, 23:30, 09:00
  // JST: 07:30, 08:30, 18:00
  minute: '30,0,0',
  hour: '22,23,9',
}),

On-demand Revalidationの活用

トップページとニュースページの記事は、高頻度で更新されるわけではないため、都度fetchするのではなく、ISRとしています。とはいっても、既述のスケジュールによって最新記事を取得し、microCMSに格納されているため、その際には該当記事をユーザーへ提供したいです。
そのため、microCMSのWebhookも検討しましたが、RSS収集バッチのロジック内でNext.jsの機能であるOn-demand Revalidationの機構を活用しました。

On-demand Revalidationは、あくまでキャッシュを無効化するのみであり、無効化後にユーザーからアクセス(リクエスト)があった場合に最新の内容にてキャッシュが再作成されてコンテンツが提供できるという仕組みです。つまり、強制的にISRをさせることにしました。

バッチ

async function triggerRevalidation(): Promise<void> {
  try {
    const appApiUrl = await getSecureParameter(process.env.APP_API_URL_PARAM!);
    const appApiKey = await getSecureParameter(process.env.APP_API_KEY_PARAM!);

    if (!appApiUrl || !appApiKey) {
      throw new Error('Missing required API URL or API Key for revalidation');
    }

    const response = await axios.post(
      `${appApiUrl}/api/revalidate`,
      {},
      {
        headers: {
          'Content-Type': 'application/json',
          'X-API-Key': appApiKey,
        },
        timeout: 10000,
      }
    );

    console.log(`Revalidation triggered:`, response.data);
  } catch (error) {
    console.error('Revalidation failed:', error);
  }
}

export const handler = async () => {
  const items = await runCollector();
  console.log(`collected ${items.length} items`);

  if (items.length) {
    await saveMany(items);
    console.log(`saved ${items.length} items`);

    // ISRを強制的に実行
    await triggerRevalidation();
  }

  return { saved: items.length };
};

LambdaのRSS収集バッチにて、Next.js内のRoute HandlersにあるAPIをコールしています。

バックエンド

const postHandler: HandlerFunction = (req: NextRequest) => {
  try {
    // トップページを再検証(ニュースデータを表示しているため)
    revalidatePath("/");

    // ニュース一覧ページを再検証
    revalidatePath("/news");

    // カテゴリページを再検証
    revalidatePath("/news/category/[categoryId]", "page");

    return NextResponse.json({
      revalidated: true,
      paths: ["/", "/news", "/news/category/[categoryId]"],
      timestamp: new Date().toISOString(),
    });
  } catch (e) {
    return allErrorHandler(e, req);
  }
};

export const POST = withAuthorization(postHandler);

パスを明示してキャッシュを無効化させるrevalidatePathの機構を使って、On-demand Revalidationを実行しています。

なお、上記のサンプルコードは認証部分をラップするなどカスタマイズしています。
詳細は前回の記事に記載がありますので、よろしければご確認くださいませ。

https://zenn.dev/matisse_dev/articles/87508d52dad946

ブロックチェーンのアップグレード情報

Ethereum Blogからも収集している理由としては、2025年5月にペクトラ(Pectra)へのアップグレードがありましたが、このような情報をいち早く情報をキャッチするためです。

投資家視点でも気になる情報かと思いますが、アップグレードによってブロックチェーン基盤ではハードフォークが実施されることになるので、新旧仕様のブロックチェーンが存続することになった場合には、新たな仮想通貨が生まれる可能性はゼロではないので、仮想通貨取引所やweb3事業者、エンジニアにとっては欠かせない情報となります。

https://blog.ethereum.org/2025/04/23/pectra-mainnet

なお、2025年12月3日にフサカ(Fusaka)へのアップグレードも予定されています。

https://blog.ethereum.org/2025/11/06/fusaka-mainnet-announcement

投票機能

web3でよく見る投票機能を入れて、ユーザー参加型の機能を取り入れました。
毎週ひとつの質問を提供し、収集した投票結果を翌週に発表するようにしています。

コミュニティ機能

X APIを活用したXポスト収集バッチにて、#Web3Gallery, #Web3ギャラリーのハッシュタグのポストを取得し、Web3 Garally内に表示するようにしています。

↓ はサンプルです。

学習コンテンツ

私自身もweb3に精通しているわけではないので、自身の知見整理や学習も兼ねて、YouTubeを開設して動画コンテンツを作っています。そのコンテンツをアプリ内に組み込んでいます。
とはいっても、半年前に2本作ったままになっているので、新作を作らねばならぬ(動画作るのは大変であることがわかりました...)。

https://www.youtube.com/@web3-gallery

おひとりさまスクラム開発

案の定、途中からグダグダになってしまったのですが、JIRAでチケット管理をして、2週間サイクルのおひとりさまスクラム開発で進めました。

具体的には、特別なことはしていませんが、ユーザーストーリーベースにてEpicを作成し、紐づくチケットにストーリーポイントを割り当てて進めました。

今後のロードマップ

ご意見伺いたいです!!

下記のようなキーワードでぼんやりとした構想はありますので、少しずつ形にしていきたいと思います。
とはいっても、Web3ギャラリー自体の認知度を上げることが先決ですね。

  • ユーザー登録
    • ウォレット登録
  • 週替わりNFT発行
    • NFTホルダーに対してのイベント
  • Web3ギャラリーコミュニティーの形成
  • 独自コンテンツの提供
    • 専門家インタビューや解説記事
  • X to Earnのひとつとして、Web3ギャラリー to Earnの提供
  • プロジェクト広告枠
  • スポンサー記事
  • アフリエイト
    • 取引所

おわりに

AIの活用によって、個人開発に関しても実装の省力化を図ることができ、アイデアを形にするまでの時間が短縮できることを感じることができました。
web3は来る来ると言われて数年経過し、横ばい状態が続いているかと思いますが、web3の発展に本アプリや取り組みが少しでも貢献できればと思っております。

JPYCのステーブルコインがローンチされたこともあり、国内外におけるステーブルコインは今後発展していきそうですね!

https://x.com/se_yan_engineer/status/1938883079311143090?s=20

最後までお読みいただきまして、ありがとうございました!!!

Discussion