実践応用:データ自動収集と通知送信
前章では、様々なスケジューリングの「実行基盤」(Linuxホスト、GitHub Actions、Vercel Cronなど)を学びました。ここではそれらを統合し、実世界で最も一般的なビジネスシナリオである**「日次プッシュ通知システム」**を実装します。
シナリオ説明
「仮想通貨相場日報」システムを構築します。このプログラムは毎朝8時に以下の3つの処理を実行します:
- 無料のSupabaseデータベースからサービス登録ユーザー(Line Notifyトークン)を取得
- 外部APIを呼び出して最新のビットコイン価格を取得
- ループ処理で最新レートをLine Notify経由で全登録者に配信
このスクリプトはGitHub Actionsで実行することも、Next.js APIとしてVercelにデプロイすることも可能です。ここではNode.js(TypeScript)スクリプトを例に説明します。
事前準備
- Supabaseデータベース:
subscribersテーブルを作成(id(主キー)、line_token(文字列)の2カラム) - Line Notify:Line Notify公式サイトで個人アクセストークンを取得し、Supabaseデータベースに登録
コア実装コード
必要なパッケージをインストール:
npm install @supabase/supabase-js axios
メインスクリプトdaily-job.ts:
import { createClient } from '@supabase/supabase-js';
import axios from 'axios';
// ==========================================
// 1. 環境変数設定
// ==========================================
// 本番環境ではprocess.envで読み取り、ハードコーディングしないこと!
const SUPABASE_URL = process.env.SUPABASE_URL || 'https://your-project.supabase.co';
const SUPABASE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || 'your-service-role-key';
// Supabaseクライアント初期化(RLSを回避するためService Role Key使用)
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
// ==========================================
// 2. ビットコイン価格取得関数
// ==========================================
async function getBitcoinPrice(): Promise<number | null> {
try {
const response = await axios.get('https://api.coindesk.com/v1/bpi/currentprice.json');
const priceStr = response.data.bpi.USD.rate;
// 桁区切りカンマを除去し数値変換
return parseFloat(priceStr.replace(/,/g, ''));
} catch (error) {
console.error('価格取得失敗:', error);
return null;
}
}
// ==========================================
// 3. Line Notify送信関数
// ==========================================
async function sendLineNotify(token: string, message: string) {
try {
await axios.post(
'https://notify-api.line.me/api/notify',
`message=${encodeURIComponent(message)}`,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${token}`
}
}
);
console.log('✅ 送信成功');
} catch (error) {
console.error('❌ 送信失敗:', error);
}
}
// ==========================================
// 4. メイン処理(スケジュールジョブ)
// ==========================================
async function runDailyJob() {
console.log('🚀 ��次タスク実行開始...');
// ステップA:データ取得
const price = await getBitcoinPrice();
if (!price) {
console.log('価格取得不可、処理中止');
return;
}
const message = `\nおはようございます!💰\n本日のビットコイン相場:$${price} USD\n良い投資を!`;
console.log('送信メッセージ:', message);
// ステップB:登録者一覧取得
const { data: subscribers, error } = await supabase
.from('subscribers')
.select('line_token');
if (error || !subscribers) {
console.error('登録者データ取得エラー:', error);
return;
}
console.log(`登録者数 ${subscribers.length} 名、一斉送信開始...`);
// ステップC:ループ処理で個別送信
for (const sub of subscribers) {
if (sub.line_token) {
// Lineのレートリミット回避のため遅延処理
await new Promise(resolve => setTimeout(resolve, 500));
await sendLineNotify(sub.line_token, message);
}
}
console.log('🎉 日次タスク完了!');
}
// 実行
runDailyJob();
実装上の注意点
本スクリプトをGitHub Actionsやサーバーで運用する際、特に注意すべき3つのポイント:
-
APIレートリミット: 73行目で示したように、登録者が5人から5000人に増えた場合、
Promise.allで5000リクエストを同時送信すると、LineからDDoS攻撃とみなされトークンが永久ブロックされます。ループ内に遅延処理を追加するか、キューシステムの導入が必須です。 -
SupabaseのService Role Key: スケジュールスクリプトはバックエンドで自動実行されるため、RLSを回避するには
ANON_KEYではなくSERVICE_ROLE_KEYが必要です。このキーは絶対にフロントエンドに露出させず、環境変数で管理してください。 -
エラーハンドリングとリトライ機構: 自動スクリプトは無人状態で実行されます。APIがメンテナンス中の場合、スクリプトはクラッシュします。全てのネットワークリクエストをtry-catchで囲み、失敗時には管理者に通知する仕組みを実装しましょう。
まとめ
おめでとうございます!これで「自動スケジューリング」の核心をマスターしました。基礎的なLinux Crontabから、クラウド時代のGitHub Actions、Vercel Cronまでを網羅し、実際のビジネス価値を生む日次通知システムを構築できました。
これらの技術を組み合わせれば、自動チケット購入システム、データベースバックアップスクリプト、一人運営のメールマガジンプラットフォームなども実現可能です。これこそがプログラミングの真髄です:一度コードを書けば、コンピュータが永遠に働き続けてくれます!