みんなの空き時間、重なるところだけすぐ見える。
AkiMatch は、予定がばらばらな学生グループ向けの日程調整アプリです。幹事が候補時間を大量に作るのではなく、対象期間を共有し、参加者が自分の空いている時間帯を回答します。集計結果は日付ごとの横バー型ヒートマップで表示し、どの日のどの時間帯に人が集まりやすいかをすばやく判断できます。
ポートフォリオとしては、認証なしで共有できる日程調整フロー、Cloudflare D1 を使った回答保存、人数順の候補抽出、スマホでも読みやすい集計 UI を実装したプロダクトです。
- 認証なしでイベント URL を共有できる
- 幹事がイベント名と対象期間を入力してイベントを作成できる
- 参加者が名前と空き時間を回答できる
- 同じイベント内で同じ名前が再回答された場合、前回の回答を上書きする
- 参加可能人数が多い順に上位 3 件の候補時間を表示する
- 日付ごとの横バー型ヒートマップで時間帯の重なりを確認できる
- 1 時間単位、0:00 から 24:00 までの時間帯を集計する
- 大学生の少人数グループ幹事
- ゼミ、グループ課題、サークル、友達グループなどで集まる時間を決めたい人
- 授業、バイト、サークル、通学時間で予定がばらけやすく、日付だけでは調整しきれないグループ
丸バツ表だけでは、みんなの空き時間がどこで重なっているのか分かりにくいことがあります。
会社の飲み会のように「日付だけ決まれば、時間はだいたい終業後」で済むケースではなく、AkiMatch は「同じ日でも空いている時間が人によって違う」ケースを重視しています。
調整さんに近いシンプルな日程調整体験を保ちつつ、AkiMatch では候補時間を幹事が細かく作り込まなくても、回答結果から重なりやすい時間帯を見つけられることを重視しています。
- 幹事は対象期間を決めて URL を共有する
- 参加者は自分が空いている日と時間帯を入力する
- 集計結果は日付ごとの横バー型ヒートマップで表示する
- 上位候補時間を人数順に表示し、最適な時間帯をすぐ判断できるようにする
- 大規模イベントの出欠管理
- 店予約、会費、場所決めなどの飲み会幹事機能
- Google Calendar などの外部カレンダー連携
- 認証、アカウント、パスワード付きイベント
- イベント編集、削除、LINE 共有
- 30 分単位の詳細調整
- Next.js App Router
- React
- Tailwind CSS v4
- shadcn / Base UI
- Drizzle ORM
- Cloudflare Workers / Pages 想定
- Cloudflare D1
- OpenNext Cloudflare adapter
- Wrangler
アプリケーションは Cloudflare Workers runtime、Cloudflare D1、Drizzle ORM を前提にしています。Cloudflare への Next.js デプロイには OpenNext Cloudflare adapter を使います。
依存関係をインストールします。
pnpm install開発サーバーを起動します。
pnpm devpnpm dev はローカル D1 のマイグレーションを適用してから Next.js を起動します。初回起動時も、開発用のデータベーススキーマが用意された状態で作業できます。
ログを logs/dev.log に残しながら起動したい場合は、次のコマンドを使います。
pnpm run dev:log通常の確認では次のコマンドを使います。
pnpm lint
pnpm buildCloudflare Workers 向けのビルド結果まで確認する場合は、OpenNext Cloudflare adapter のビルドも実行します。
pnpm exec opennextjs-cloudflare buildローカルで Workers runtime のプレビューを確認する場合は、次のコマンドを使います。
pnpm run previewこのプロジェクトは、OpenNext Cloudflare adapter を使って Next.js アプリを Cloudflare Workers にデプロイします。
本番デプロイは GitHub Actions で行います。main へのプルリクエストではチェックのみを実行し、プルリクエストがマージされて main に push されたときに、同じチェックを通したうえで既存の akimatch Worker にデプロイします。
pnpm wrangler login
pnpm wrangler whoamipnpm wrangler d1 create akimatch_db返ってきたデータベース UUID を wrangler.toml の database_id に設定します。
[[d1_databases]]
binding = "DB"
database_name = "akimatch_db"
database_id = "<returned database_id>"
migrations_dir = "drizzle"D1 マイグレーションはデプロイワークフローでは自動適用しません。マイグレーションが必要なときは、手動で本番 D1 に適用します。
pnpm wrangler d1 migrations apply akimatch_db --remotepnpm run previewデプロイ変更を main にマージする前に、リポジトリ secrets に次の値を設定します。
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
ワークフローは wrangler.toml を使うため、本番 Worker は akimatch、本番 D1 データベースは akimatch_db として扱われます。
Cloudflare の DDoS managed protection は有効化した状態で運用します。加えて、Cloudflare Dashboard で Rate Limiting Rule を作成します。
- 対象パスは
/と/e/*の origin 到達リクエスト - 初期値は同一 IP から 1 分あたり 30-60 リクエスト超を目安にする
- まずは
Logで 24-48 時間確認し、その後Managed ChallengeまたはBlockに変更する - mitigation duration は 10 分程度から始める
- Cloudflare plan により method や header 条件が使えない場合は、path ベースの緩めの設定から始める
アプリ側では Server Actions の body size を 100kb に下げ、イベント期間と回答スロット数にも上限を設けています。
プルリクエストを main にマージするか、GitHub Actions から Deploy ワークフローを手動実行します。ワークフローでは次の確認を行います。
pnpm lint
pnpm build
pnpm exec opennextjs-cloudflare buildwrangler deploy まで進むのは、main への push と workflow_dispatch のみです。プルリクエストではデプロイしません。
緊急時のローカルデプロイ用に pnpm run deploy も残しています。ただし通常の本番デプロイは、デプロイされたコミットを追跡できるように GitHub Actions 経由で行います。
必要に応じて、デプロイ後に最新の Worker デプロイ状態を確認します。
pnpm wrangler deployments status --name akimatch --jsonデプロイ後は、生成された Workers URL で次の流れを確認します。
- イベントを作成する
- 共有用の
/e/{uuid}URL を開く - 参加者として空き時間を回答する
- 同じ参加者名でもう一度回答し、前回の回答が上書きされることを確認する
- 上位候補と横バー型ヒートマップが更新されることを確認する