デプロイは Vercel CLI だけで完結する(GUI 不要)。monorepo の 1 app を公開し、任意ドメインを Cloudflare か Route53 で向ける。定期実行は @event/core の withCronAuth で保護する。完全手順は レシピ deploy-vercel、ここは要点と cron を解説する。
Vercel CLI デプロイ(monorepo)
Root Directory は CLI から設定できないので、repo root に vercel.json を置いて回避する:
{
"framework": "nextjs",
"installCommand": "yarn install --frozen-lockfile",
"buildCommand": "yarn turbo build --filter=<app>",
"outputDirectory": "apps/<app>/.next"
}
罠 2 つ: ① Root Directory を CLI で指定できない → 上の vercel.json で解決。② ドメインはそれを所有する Vercel team の scope でしか add できない(別 team だと 403 domain_not_owned)。vercel deploy --prod --scope <team>、vercel domains add <domain> --scope <team>。DNS は A 76.76.21.21 を 1 本 (Cloudflare は DNS-only で証明書発行を通してから Proxied 化)。
cron 保護 — withCronAuth / isCronAuthorized
vercel.json の crons[] で叩く定期エンドポイントは、必ず @event/core の withCronAuth で包む。 Authorization: Bearer ${CRON_SECRET} の厳格一致のみを通す:
import { withCronAuth } from "@event/core";
// app/api/cron/sync/route.ts
export const GET = withCronAuth(async (req) => {
// …定期同期処理…
return Response.json({ ok: true });
});
// vercel.json
{ "crons": [{ "path": "/api/cron/sync", "schedule": "0 * * * *" }] }
CRON_SECRET未設定なら503 CRON_SECRET not configured(誤って無防備公開しない fail-closed)- 判定だけ欲しいときは
isCronAuthorized(req, { secret? })(既定 envCRON_SECRET) - Vercel Cron は自動で Bearer を付与する。手動叩きは
curl -H "Authorization: Bearer $CRON_SECRET"
セキュリティヘッダはデプロイ時に自動
createNextConfig(第40章)が CSP / HSTS / COOP / Permissions-Policy を全レスポンスに付与する。 デプロイ後 curl -I https://<domain> で content-security-policy 等が出ることを確認する。新しい外部ホスト (分析・フォント)を足したら next.config.ts の security.csp フラグで許可する(ハードコードしない)。
本番反映のルール
vercel git connect で GitHub を繋ぐと main への push = 本番反映。作業は必ずブランチ → PR。Vercel は squash/rebase で author 保持が崩れると deploy blocked になることがあるため merge commit 推奨。
observability(現状)
専用の observability パッケージは持たない。実装済みは createNextConfig の removeConsole(本番は error/warn を残して他を除去)と Vercel 標準の Build/Runtime Logs。構造化ログ・トレースの導入は 将来の拡張ポイント(現状は未実装)。