Codelab: プッシュ通知サーバーを構築する

Kate Jeffreys
Kate Jeffreys
Kayce Basques
Kayce Basques

この Codelab では、プッシュ通知サーバーを構築する手順を説明します。この Codelab を終了するまでに、次の機能を備えたサーバーが完成します。

  • プッシュ通知の登録を追跡します(クライアントがプッシュ通知を有効にするとサーバーが新しいデータベース レコードを作成し、クライアントがプッシュ通知を無効にすると既存のデータベース レコードを削除します)。
  • 単一のクライアントにプッシュ通知を送信します
  • 登録しているすべてのクライアントにプッシュ通知を送信します

この Codelab は、実践を通して学習することを目的としており、コンセプトについてはあまり説明しません。プッシュ通知のコンセプトについては、プッシュ通知の仕組みをご覧ください。

この Codelab のクライアント コードはすでに完成しています。この Codelab では、サーバーのみを実装します。プッシュ通知クライアントを実装する方法については、Codelab: プッシュ通知クライアントを構築するをご覧ください。

ブラウザの互換性

この Codelab は、次のオペレーティング システムとブラウザの組み合わせで動作することが確認されています。

  • Windows: Chrome、Edge
  • macOS: Chrome、Firefox
  • Android: Chrome、Firefox

この Codelab は、次のオペレーティング システム(またはオペレーティング システムとブラウザの組み合わせ)では動作しないことがわかっています。

  • macOS: Brave、Edge、Safari
  • iOS

アプリケーション スタック

  • サーバーは Express.js をベースに構築されています。
  • web-push Node.js ライブラリは、プッシュ通知のロジックをすべて処理します。
  • サブスクリプション データは、lowdb を使用して JSON ファイルに書き込まれます。

プッシュ通知を実装するために、これらのテクノロジーを使用する必要はありません。これらのテクノロジーは、信頼性の高い Codelab エクスペリエンスを提供するため、選択しました。

セットアップ

認証を設定する

プッシュ通知を機能させるには、認証鍵を使用してサーバーとクライアントを設定する必要があります。理由については、ウェブ プッシュ プロトコル リクエストに署名するをご覧ください。

  1. ターミナルを開きます。
  2. ターミナルで npx web-push generate-vapid-keys を実行します。秘密鍵と公開鍵の値をコピーします。
  3. .env を開き、VAPID_PUBLIC_KEYVAPID_PRIVATE_KEY を更新します。VAPID_SUBJECTmailto:test@test.test に設定します。これらの値はすべて二重引用符で囲む必要があります。更新後、.env ファイルは次のようになります。
VAPID_PUBLIC_KEY="BKiwTvD9HA…"
VAPID_PRIVATE_KEY="4mXG9jBUaU…"
VAPID_SUBJECT="mailto:test@test.test"
  1. public/index.js を開きます。
  2. VAPID_PUBLIC_KEY_VALUE_HERE は、公開鍵の値に置き換えます。

サブスクリプションを管理

サブスクリプション プロセスのほとんどはクライアントが処理します。サーバーの主な役割は、新しいプッシュ通知の定期購入を保存し、古い定期購入を削除することです。これらのサブスクリプションにより、今後クライアントにメッセージをプッシュできます。サブスクリプション プロセスの詳細については、クライアントをプッシュ通知に登録するをご覧ください。

新しい定期購入情報を保存する

  1. アプリタブで [サービス ワーカーを登録] をクリックします。ステータス ボックスに次のようなメッセージが表示されます。
Service worker registered. Scope: https://example.com
  1. アプリのタブで [Subscribe to push] をクリックします。ブラウザまたはオペレーティング システムから、ウェブサイトによるプッシュ通知の送信を許可するかどうかを確認するメッセージが表示されることがあります。[許可](または、ブラウザや OS で使用されている同等のフレーズ)をクリックします。ステータス ボックスに次のようなメッセージが表示されます。
Service worker subscribed to push.  Endpoint: https://fcm.googleapis.com/fcm/send/…
  1. ターミナルを開いてログを確認します。/add-subscription の後にデータが表示されます。/add-subscription は、クライアントがプッシュ通知の登録を希望するときに POST リクエストを送信する URL です。次のデータは、保存する必要があるクライアントの定期購入情報です。
  2. server.js を開きます。
  3. /add-subscription ルート ハンドラのロジックを次のコードで更新します。
app.post('/add-subscription', (request, response) => {
  console.log('/add-subscription');
  console.log(request.body);
  console.log(`Subscribing ${request.body.endpoint}`);
  db.get('subscriptions')
    .push(request.body)
    .write();
  response.sendStatus(200);
});

古い定期購入情報を削除する

  1. アプリのタブに戻ります。
  2. [プッシュ通知の登録を解除] をクリックします。
  3. ログをもう一度確認します。/remove-subscription の後にクライアントの定期購入情報が表示されます。
  4. /remove-subscription ルート ハンドラのロジックを次のコードで更新します。
app.post('/remove-subscription', (request, response) => {
  console.log('/remove-subscription');
  console.log(request.body);
  console.log(`Unsubscribing ${request.body.endpoint}`);
  db.get('subscriptions')
    .remove({endpoint: request.body.endpoint})
    .write();
  response.sendStatus(200);
});

通知を送信する

プッシュ メッセージを送信するで説明したように、サーバーはプッシュ メッセージをクライアントに直接送信しません。代わりに、プッシュ サービスに依存しています。基本的には、ユーザーが使用しているブラウザ ベンダーが所有するウェブサービス(プッシュ サービス)にウェブ サービス リクエスト(ウェブプッシュ プロトコル リクエスト)を行うことで、クライアントにメッセージをプッシュするプロセスを開始するだけです。

  1. /notify-me ルート ハンドラのロジックを次のコードで更新します。
app.post('/notify-me', (request, response) => {
  console.log('/notify-me');
  console.log(request.body);
  console.log(`Notifying ${request.body.endpoint}`);
  const subscription = 
      db.get('subscriptions').find({endpoint: request.body.endpoint}).value();
  sendNotifications([subscription]);
  response.sendStatus(200);
});
  1. sendNotifications() 関数を次のコードで更新します。
function sendNotifications(subscriptions) {
  // TODO
  // Create the notification content.
  const notification = JSON.stringify({
    title: "Hello, Notifications!",
    options: {
      body: `ID: ${Math.floor(Math.random() * 100)}`
    }
  });
  // Customize how the push service should attempt to deliver the push message.
  // And provide authentication information.
  const options = {
    TTL: 10000,
    vapidDetails: vapidDetails
  };
  // Send a push message to each client specified in the subscriptions array.
  subscriptions.forEach(subscription => {
    const endpoint = subscription.endpoint;
    const id = endpoint.substr((endpoint.length - 8), endpoint.length);
    webpush.sendNotification(subscription, notification, options)
      .then(result => {
        console.log(`Endpoint ID: ${id}`);
        console.log(`Result: ${result.statusCode}`);
      })
      .catch(error => {
        console.log(`Endpoint ID: ${id}`);
        console.log(`Error: ${error} `);
      });
  });
}
  1. /notify-all ルート ハンドラのロジックを次のコードで更新します。
app.post('/notify-all', (request, response) => {
  console.log('/notify-all');
  response.sendStatus(200);
  console.log('Notifying all subscribers');
  const subscriptions =
      db.get('subscriptions').cloneDeep().value();
  if (subscriptions.length > 0) {
    sendNotifications(subscriptions);
    response.sendStatus(200);
  } else {
    response.sendStatus(409);
  }
});
  1. アプリのタブに戻ります。
  2. [通知を受け取る] をクリックします。プッシュ通知が届きます。タイトルは Hello, Notifications!、本文は ID: <ID> とします。ここで、<ID> は乱数です。
  3. 他のブラウザやデバイスでアプリを開き、プッシュ通知に登録してから、[すべてに通知] ボタンをクリックしてみてください。登録したすべてのデバイスで同じ通知を受け取ります(プッシュ通知の本文の ID は同じになります)。

次のステップ

  • プッシュ通知の仕組みについて詳しくは、プッシュ通知の概要をご覧ください。
  • Codelab: プッシュ通知クライアントを構築するで、通知権限をリクエストし、プッシュ通知を受信するためにデバイスを登録し、サービス ワーカーを使用してプッシュ メッセージを受信して通知として表示するクライアントを構築する方法をご確認ください。