Web Pushはモバイルアプリだけのものではない
多くの開発者は、ブラウザにプッシュ通知(push notification)を送るにはFirebase Cloud Messaging (FCM)を使うのが当たり前だと思っています。しかし実際には、サードパーティに依存せず、インフラを完全にコントロールできるより「純粋な」解決策があります。それがW3C標準のWeb Pushプロトコルです。
以前、1日あたり約1,000件の注文を処理する物流管理ダッシュボードを担当したことがあります。当時、Firebase SDKを統合したことで、通知機能のためだけにフロントエンドのバンドルサイズが200KB近く増加しました。Node.jsのweb-pushライブラリに切り替えた後、チームはこの負担を完全に排除できました。ページ読み込み速度が大幅に向上し、Google Consoleでの複雑なセットアップも不要になったため、デプロイプロセスも簡素化されました。
実装方法の比較
プッシュ通知を統合する際、主に2つのアプローチを検討することになります:
- Firebase Cloud Messaging (FCM): Googleが仲介役となります。インフラはGoogleが担当しますが, 代わりに重いSDKを埋め込み、彼らの厳格なポリシーに従う必要があります。
- Web Pushプロトコル (VAPID): これは直接的なアプローチです。VAPID(Voluntary Application Server Identification)キーペアを使用して、ChromeやFirefoxに組み込まれているPush Serviceに対してサーバーを認証します。
なぜ web-push はBackend Node.jsにおいて最適な選択なのか?
各ソリューションにはそれぞれの利点があります。しかし、軽量さとコントロール権を優先する場合、web-pushは非常に実用的なメリットをもたらします:
主なメリット:
- 超軽量なバンドルサイズ: 巨大なSDKを埋め込む必要はありません。フロントエンドはイベントを待機するための数行の純粋なService Workerだけで済みます。
- データセキュリティ: ユーザーの購読情報(subscription)は自前のデータベースに保存されます。あなたとブラウザ以外, 誰もその情報を知り得ません。
- コスト削減: 完全に無料です。クォータ(制限)を超えたり、クラウドプロバイダーによる突然の価格改定を心配する必要はありません。
- 自由なカスタマイズ: ペイロードの暗号化方法や通知の有効期限(TTL)を完全にコントロールできます。
注意点:
- ユーザーの
endpointとkeysを保存するためのデータベーステーブルを自分で設計する必要があります。 - 送信失敗時のリトライ(再試行)ロジックを実装する必要があります。
Web Pushの仕組み
スムーズに実装するために、データの流れを理解しましょう。データはサーバーからブラウザへ直接送られるのではなく、中継局を経由します:
- 購読 (Subscription): ブラウザがユーザーに権限を求めます。許可されると、Push Service(GoogleやMozillaが管理)のURLを含む
subscriptionオブジェクトを返します。 - 保存: フロントエンドはこのオブジェクトをNode.jsサーバーに送り、データベース(MongoDBやPostgreSQLなど)に保存します。
- プッシュ: 新しいイベントが発生すると、サーバーは
web-pushライブラリを使用してVAPIDキーで署名し、Push Serviceに送信します。Push Serviceは、ユーザーがウェブサイトを閉じていても通知を配信します。
詳細な実装ガイド
それではコードを書いていきましょう。Node.jsがインストールされたPCがあれば準備完了です。
ステップ1:プロジェクトの初期化とVAPIDキーの生成
まず、ディレクトリを作成し、必要なパッケージをインストールします:
mkdir web-push-tutorial
cd web-push-tutorial
npm init -y
npm install web-push express body-parser cors
VAPIDキーペアは、サーバーの電子署名のような役割を果たします。次のコマンドで素早く生成できます:
./node_modules/.bin/web-push generate-vapid-keys
Public Key と Private Key をコピーしておいてください。これらは次のステップで使用します。
ステップ2:Node.jsサーバーの構築
server.js ファイルを作成します。これが通知送信を制御する「脳」となります。
const webpush = require('web-push');
const express = require('express');
const app = express();
app.use(require('cors')());
app.use(require('body-parser').json());
const publicVapidKey = 'YOUR_PUBLIC_KEY';
const privateVapidKey = 'YOUR_PRIVATE_KEY';
webpush.setVapidDetails(
'mailto:[email protected]',
publicVapidKey,
privateVapidKey
);
app.post('/subscribe', (req, res) => {
const subscription = req.body;
res.status(201).json({});
const payload = JSON.stringify({
title: 'システム通知',
body: '注文 #1234 が確定されました!',
});
webpush.sendNotification(subscription, payload)
.catch(err => console.error('プッシュ送信失敗:', err));
});
app.listen(5000, () => console.log('サーバーがポート5000で起動しました'));
ステップ3:クライアントとService Workerの設定
フロントエンドでは、client.js がブラウザへの登録を担当します。
const publicVapidKey = 'YOUR_PUBLIC_KEY';
async function subscribeUser() {
// Service Workerを登録
const register = await navigator.serviceWorker.register('/sw.js', { scope: '/' });
// 購読(subscription)を作成
const subscription = await register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
});
// サーバーへ情報を送信
await fetch('http://localhost:5000/subscribe', {
method: 'POST',
body: JSON.stringify(subscription),
headers: { 'content-type': 'application/json' }
});
}
最後は sw.js です。このファイルはバックグラウンドで動作し、通知ポップアップを表示します:
self.addEventListener('push', e => {
const data = e.data.json();
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icon.png'
});
});
エラーを避けるための実践的な経験
このシステムを本番環境(production)に導入する際、3つの重要なポイントに注意してください:
- HTTPSが必須: ブラウザは、localhostを除き、HTTPで動作するサイトではService Workerを拒否します。
- データベースのクリーンアップ: ユーザーが通知をブロックすると、Push Serviceは404または410エラーを返します。リソースの無駄を避けるため、その購読情報をすぐにデータベースから削除する必要があります。
- ペイロードの制限: メッセージの最大サイズは4KBです。大量のデータやbase64画像をここに含めないようにしてください。
Web Pushシステムの自作は、思っているほど難しくありません。Googleのエコシステムに依存することなく、絶対的な柔軟性を提供し、アプリケーションをよりプロフェッショナルなものにします。ぜひ活用してみてください!

