1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Chromebookだけで、毎朝AIがIT・AIトレンドをまとめてくれるサイトを作った【第3回:Supabaseに保存してVercelで毎朝自動実行する】

1
Last updated at Posted at 2026-09-30

はじめに

前回:第2回:RSS取得とGeminiで要約する

要約はできるようになりました。でも、このままだと問題があります。

ページを開くたびにRSSを読みに行き、ボタンを押すたびにGeminiで要約し直す。しかも、毎回ターミナルを開いて開発サーバーを起動しないと見られない。これでは「毎朝開くだけ」にはほど遠い状態です。

今回は、記事と要約をSupabaseに保存し、Vercelで公開して、毎朝自動でおすすめが作られるようにするところまでを書きます。


こんな方におすすめ

  • Next.jsとSupabaseで、重複なくデータを保存したい方
  • Vercelの無料プランで、毎日決まった時間に処理を動かしたい方
  • 個人用のサイトに、手軽に鍵(アクセス制限)をかけたい方
  • Chromebookから、GitHub経由でVercelにデプロイしたい方

「取得」と「表示」を分ける

最初に決めたのは、RSSを取得するタイミングと、ページを見るタイミングを分けることです。

  • 取得:1日1回、RSSを読みに行って新しい記事だけ保存する
  • 表示:ページを開いたら、保存済みのデータを表示するだけ
    こうしておくと、ページを何度開いてもRSSやGeminiは使いません。要約もデータベースに保存しておけば、一度要約した記事で二度とAPIを使わずに済みます。

Supabaseのテーブル設計

記事は1つのテーブルにまとめました。あとで使う列も最初から用意しておくと、テーブルを作り直さずに済みます。

create table articles (
  id bigint generated always as identity primary key,
  link text not null unique,   -- 同じURLは二重に入らない
  title text not null,
  snippet text not null default '',
  source_name text not null,
  bookmarks integer,
  published_at timestamptz,
  fetched_at timestamptz not null default now(),
  summary text,                -- AIの要約
  keywords jsonb,              -- キーワードと解説
  reason text,                 -- おすすめ理由
  recommended_on date          -- どの日のおすすめか
);
 
alter table articles enable row level security;

ポイントは link を unique にしていることです。保存時に ignoreDuplicates を付けると、すでにある記事は自動でスキップされ、新しい記事だけが入ります。

lib/ingest.ts
await supabase
  .from("articles")
  .upsert(rows, { onConflict: "link", ignoreDuplicates: true });

また、行レベルセキュリティ(RLS)を有効にしてポリシーを作らないことで、サーバー側のシークレットキー以外からは読み書きできないようにしています。シークレットキーはブラウザに出ないよう、NEXT_PUBLIC_ を付けずにサーバー側だけで使います。


毎朝の処理を1つにまとめる

「記事を取得 → おすすめを選ぶ → 要約して保存」を1つの関数にまとめ、/api/daily から呼べるようにしました。

大事にしたのは、同じ日に何回実行しても結果が変わらないことです。その日のおすすめがすでにあれば、Geminiを使わずに終わります。自動実行が2回走っても、手動で押しても、無料枠を無駄にしません。

lib/daily.ts
const { count } = await supabase
  .from("articles")
  .select("id", { count: "exact", head: true })
  .eq("recommended_on", today);
if (count) return { message: "今日のおすすめは作成済みです" };

Vercelで毎朝自動実行する

VercelのCron機能を使うと、決まった時間にAPIを呼び出してくれます。設定は vercel.json に書くだけです。

vercel.json
{
  "crons": [
    { "path": "/api/daily", "schedule": "0 21 * * *" }
  ]
}

注意点は2つあります。

  • 時刻はUTCなので、0 21 * * * は日本時間の朝6時
  • 無料のHobbyプランは1日1回までで、実行時刻に最大1時間ほどのずれがある
    今回は「朝開いたらできている」で十分なので、1日1回で問題ありませんでした。

また、/api/daily はインターネットに公開されるので、他人に叩かれるとGeminiの無料枠が減ってしまいます。CRON_SECRET という環境変数を登録しておくと、Vercelが自動実行時に合言葉を添えてくれるので、それを確認して弾くようにしています。

app/api/daily/route.ts
const secret = process.env.CRON_SECRET;
if (secret && request.headers.get("authorization") !== `Bearer ${secret}`) {
  return Response.json({ error: "Unauthorized" }, { status: 401 });
}

サイト全体に鍵をかける

個人用のサイトなので、IDとパスワードを知っている人だけが見られるようにしました。Next.jsの proxy.ts(バージョン15以前は middleware.ts)で、ベーシック認証をかけています。

proxy.ts
export const config = {
  // 毎朝の自動実行は CRON_SECRET で守っているので、鍵の対象から外す
  matcher: ["/((?!api/daily|_next/static|_next/image|favicon.ico).*)"],
};

自動実行はIDとパスワードを入力できないので、/api/daily だけは鍵の対象から外しているのがポイントです。一度Chromeにパスワードを保存すれば、次からは自動で入れます。


Chromebookからデプロイする

GitHubへの送信には、GitHub公式のコマンドツール gh を使いました。ブラウザで承認するだけでログインでき、リポジトリの作成から送信までコマンドで済みます。

sudo apt install -y gh
gh auth login
gh repo create trend-study --private --source=. --remote=origin --push

あとはVercelでGitHubのリポジトリを読み込み、環境変数を登録すれば公開完了です。以降はGitHubに送るたびに、Vercelが自動でサイトを更新してくれます。


ハマったところ

鍵がかからない

公開したのに、サイトを開いてもパスワードを聞かれませんでした。確認すると、IDとパスワードの環境変数がVercel側にうまく連携されていなかったようです。Vercelの画面から設定し直し、再デプロイしたところ鍵がかかるようになりました。環境変数は、変更しただけでは今動いているサイトに反映されないので、変更後は再デプロイを忘れずに。

Gitの管理が始まっていなかった

create-next-app で作ったのに、なぜか git init されていませんでした。.gitignore に .env* が入っているかを git check-ignore .env.local で確認してから送ると安心です。


まとめ

  • 「取得」と「表示」を分けて、ページを開くだけではAPIを使わないようにした
  • link を unique にして、重複なく記事を保存した
  • 毎朝の処理は、同じ日に何回実行しても結果が変わらないようにした
  • Vercel Cronは無料プランだと1日1回、時刻はUTCで指定する
  • 自動実行は CRON_SECRET、サイト全体はベーシック認証で守った
    これで、朝ブックマークを開くだけで、今日のおすすめが要約付きで並ぶようになりました。次回は、カレンダー・トレンドワード・検索・総括を追加して、「読むだけで学べるサイト」にしていく話です。

次回:第4回:カレンダー・トレンドワード・検索・総括で「学べるサイト」にする

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?