knowledge base

マークアップ/フロントエンドエンジニアのWEB制作における備忘録です。平日はWEB屋、休日は社会人劇団の主宰・劇作家をしています。

【PWA】シリーズPWA (9) APIを実装する

Expressが提供するAPI自体を実装します。

基本的には第7回で紹介した内容を、Expressの文法に落とし込んでゆく感じになります。

ここで登場するsubscriptionsという変数には前回でSqualizeによって作成されたORマッパーのオブジェクト(DBにデータの追加や削除や他にもごにょごにょできるオブジェクト)が格納されています

add (レコードの追加)

app.post('/add', function(req, res) {
	let data = req.body;
	let response = res;
	subscriptions.create({
		registrationID: data.registrationID,
		publicKey: data.key,
		auth: data.auth,
		endpoint: data.endpoint
	}).then(function(){
		response.setHeader('Content-Type', 'text/plain');
		response.end('New subscriptions is successfully added.');
	});
});

delete(レコードの削除フラグをTRUEに)

クライアントからは削除するregistrationIDがわたってくる想定です。

app.post('/delete', function(req, res) {
	let data = req.body;
	subscriptions.update({
		deleted: true
	},{
	    where: {
	        registrationID: data.registrationID
	    }
	});
});

push

ここは、これまでに登場したロジックを再利用することができます。

app.post('/push', function(req, res) {
	let response = res;
	let data = req.body;
	let subscribers = [];

	// 通知の内容
	const params = {
	    title: data.title,
	    msg: data.message,
	    icon: '/path/to/icon'
	};

	webpush.setVapidDetails(
	    'admin@xxx.jp',
	    'Firebase Consoleから取得した公開鍵',
	    'Firebase Consoleから取得した秘密鍵'
	);

	subscriptions.findAll({
	    where: {
	        deleted : false
	    }
	}).then(function(rows){
		// 利用可能なサブスクリプションを配列に追加
		rows.forEach(function (row) {
			let subscription = {};
			subscription.endpoint = row.endpoint;
			subscription.expirationTime = null;
			subscription.keys = {
				p256dh: row.publicKey,
				auth: row.auth
			};
			subscribers.push(subscription);
		});
		// 利用可能なサブスクリプション全てに対して通知を発信
		Promise.all(subscribers.map(subscription => {
		    return webpush.sendNotification(subscription, JSON.stringify(params), {});
		}))
		.then(function(res){
			response.setHeader('Content-Type', 'text/plain');
			response.end('Request data is successfully pushed.');
		})
		.catch(function(err){
			console.log('ERROR', err);
		});
	});
});

これらを合わせると以下のようになります。

const express = require('express');
const webpush = require('web-push');
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();

// ルートにアクセスされたら文字列を表示
app.get('/', function(req, res) {
	res.setHeader('Content-Type', 'text/plain');
	res.send('This is PWA Test App');
});

// addでアクセスされたらサブスクリプションをDBに追加する
app.post('/add', function(req, res) {
	let data = req.body;
	let response = res;
	subscriptions.create({
		registrationID: data.registrationID,
		publicKey: data.key,
		auth: data.auth,
		endpoint: data.endpoint
	}).then(function(){
		response.setHeader('Content-Type', 'text/plain');
		response.end('New subscriptions is successfully added.');
	});
});

// deleteでアクセスされたら古いサブスクリプションの削除フラグをtrueにする
app.post('/delete', function(req, res) {
	let data = req.body;
	subscriptions.update({
	    deleted: true
	},{
	    where: {
            registrationID: data.registrationID
	    }
	});
});

// pushでアクセスされたら削除フラグがfalseのものに対して通知を送る
app.post('/push', function(req, res) {
	let response = res;
	let data = req.body;
	let subscribers = [];

	// 通知の内容
	const params = {
	    title: data.title,
	    msg: data.message,
	    icon: '/path/to/icon'
	};

	webpush.setVapidDetails(
	    'admin@xxx.jp',
	    'Firebase Consoleから取得した公開鍵',
	    'Firebase Consoleから取得した秘密鍵'
	);

	subscriptions.findAll({
	    where: {
	        deleted : false
	    }
	}).then(function(rows){
		// 利用可能なサブスクリプションを配列に追加
		rows.forEach(function (row) {
			let subscription = {};
			subscription.endpoint = row.endpoint;
			subscription.expirationTime = null;
			subscription.keys = {
				p256dh: row.publicKey,
				auth: row.auth
			};
			subscribers.push(subscription);
		});
		Promise.all(subscribers.map(subscription => {
		    return webpush.sendNotification(subscription, JSON.stringify(params), {});
		}))
		.then(function(res){
			response.setHeader('Content-Type', 'text/plain');
			response.end('Request data is successfully pushed.');
		})
		.catch(function(err){
			console.log('ERROR', err);
		});
	});
});

// サーバ起動
const server = app.listen(process.env.PORT || 8000);

次回予告

以上で、サブスクリプション をDBで管理し、有効なものにだけ通知を発信する仕組みができました。 続いて、このプログラムを叩く、クライアント側の実装を行います。