【PWA】シリーズPWA (8) 複数の端末に通知を送る
ここまでで、任意の内容でプッシュ通知を送ることはできました。
ところが現状では、今後複数のサブスクリプションが増えた時に対応ができません。
push.jsは配列にて複数のサブスクリプションを送れるようになっているため、サブスクリプション が増えること自体は問題ありません。
ところが現状push.jsにハードコーディングされているため、増えれば増えるほどpush.jsに追記をしていかなければいけません。
そこでここからさらに一歩踏み込み、サブスクリプションの情報を永続的に管理できる様に変えてゆきます。
基本的にはこちらで解説されているように、DBを利用してユーザー管理をする要領でサブスクリプション を管理します。
今回は、このデータベースとの連携部分を実装してゆきます。 サブスクリプションの登録・更新・プッシュの機能があるので、APIとして機能を提供できるようにします。
- push.jsを別環境に切り離してWebアプリ化し、登録・更新・プッシュのAPIを公開。今回はフレームワークとしてExpressを利用。
- APIを通じて受け取ったサブスクリプションの情報をDBで管理する。今回はPostgreSQLを使用。
- クライアントからはサブスクリプションの情報をヘッダーに含めて、push.jsにリクエストを送るようにする。
まずは、APIを提供するところから始めます。
ExpressでAPIを提供
まずはpush.jsを以下のような骨組みを以下のようにします。
ExpressというWebアプリケーションフレームワークを使用しますので、npmにてインストールください。
const express = require('express'); const app = express(); // addでアクセスされたらサブスクリプションをDBに追加する app.post('/add', function(req, res) { }); // deleteでアクセスされたら古いサブスクリプションの削除フラグをtrueにする app.post('/delete', function(req, res) { }); // pushでアクセスされたら削除フラグがfalseのものに対して通知を送る app.post('/push', function(req, res) { }); // サーバ起動 const server = app.listen(process.env.PORT || 8000);
Expressの文法は割愛しますが、node push.jsでサーバーが立ち上がるので、その環境(ローカルだとhttp://localhost:8000になります)に対して「/add」「/delete」「/push」というURLでアクセスが会ったときにそれぞれ何かしらの処理を割り振ることができるようになりました。
アクセス時のリクエストヘッダーにサブスクリプションの情報が入っているため、reqオブジェクトのbodyを取得できるようにしなければいけません。
ところがExpressの仕様によると現状はbodyを取得するためにはいちどパーサーを通さないとundefinedになってしまうため、以下の記述を追加します。
const express = require('express'); const bodyParser = require('body-parser'); const app = express(); // CORSを許可する app.use(function(req, res, next) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); }); // urlencodedとjsonは別々に初期化する app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); // addでアクセスされたらサブスクリプションをDBに追加する app.post('/add', function(req, res) { console.log(req.body); }); // deleteでアクセスされたら古いサブスクリプションの削除フラグをtrueにする app.post('/delete', function(req, res) { console.log(req.body); }); // pushでアクセスされたら削除フラグがfalseのものに対して通知を送る app.post('/push', function(req, res) { console.log(req.body); }); // サーバ起動 const server = app.listen(process.env.PORT || 8000);
こうすることでリクエストヘッダーのbodyを取得できるようになります。
また、クライアントからはfetch APIを使ってリクエストを投げるようにする予定なので、今のうちにCORSエラーが起きないようにしておきます。
こちらで(1)の実装は完了しました。
テーブル定義
DBとの連携部分を実装する前に、テーブル定義を行います。
以下のようなテーブルでサブスクリプションのデータを管理します。
id | registrationID | publicKey | auth | endpoint | deleted |
---|---|---|---|---|---|
INTEGER AUTO INCREMENT |
TEXT NOT NULL |
TEXT NOT NULL |
TEXT NOT NULL |
TEXT NOT NULL |
BOOLEAN NOT NULL デフォルトはFALSE |
主キー | 通知許可時にクライアントから送られる値を格納 | 同左 | 同左 | 同左 | この値によって有効なサブスクリプションかどうかを判別 |
そして通知送信時にはdeletedカラムがFALSEのものに対して通知を送るため、このカラムに対してインデックスを生成します。
DB作成・DB接続・テーブル作成
DBはプログラムでは作成せず、あらかじめPostgreSQLをインストールし、「subscriptions」という名前でDBを作成します。
その前提で、DBに接続してテーブルを作成するところからプログラムで実装します。
生のSQLを発行しても良いのですが、ここではDBごとの方言を吸収できるよう、SequalizeというORマッパーを利用します。
const express = require('express'); const bodyParser = require('body-parser'); const Sequelize = require('sequelize'); const app = express(); // CORSを許可する app.use(function(req, res, next) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); }); // urlencodedとjsonは別々に初期化する app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); // DBに接続 const sequelize = new Sequelize('接続文字列', { logging: false, operatorsAliases: false }) // テーブルを作成 const subscriptions = sequelize.define('subscriptions', { id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true }, registrationID: { type: Sequelize.STRING, allowNull: false }, publicKey: { type: Sequelize.STRING, allowNull: false }, auth: { type: Sequelize.STRING, allowNull: false }, endpoint: { type: Sequelize.STRING, allowNull: false }, deleted: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: false } }, { freezeTableName: true, timestamps: true, indexes:[{ unique: false, fields:['deleted'] }] }); subscriptions.sync(); // addでアクセスされたらサブスクリプションをDBに追加する app.post('/add', function(req, res) { }); // deleteでアクセスされたら古いサブスクリプションの削除フラグをtrueにする app.post('/delete', function(req, res) { }); // pushでアクセスされたら削除フラグがfalseのものに対して通知を送る app.post('/push', function(req, res) { }); // サーバ起動 const server = app.listen(process.env.PORT || 8000);
こちらでサーバー起動時にDBに接続し、テーブルが存在しなかったらテーブルを作成するようになりました。