Push API
Push API
หัวข้อที่มีชื่อว่า “Push API”Push API ช่วยให้ server ของคุณส่งข้อความไปยังอุปกรณ์ของผู้ใช้ได้แม้ผู้ใช้จะไม่ได้ใช้แอปอยู่ — ไม่ต้องมีแท็บเปิดอยู่ browser รักษาการเชื่อมต่อถาวรกับ push service (ที่ดูแลโดย browser vendor) server ของคุณพูดคุยกับ push service นั้น และ push service ส่งข้อความไปยังอุปกรณ์ของผู้ใช้ที่ service worker ของคุณรับข้อความนั้น
Push ทำงานอย่างไรแบบ end-to-end
หัวข้อที่มีชื่อว่า “Push ทำงานอย่างไรแบบ end-to-end”sequenceDiagram
participant Page as Page (JS)
participant SW as Service Worker
participant PS as Push Service (browser vendor)
participant Srv as Your Server
Page->>SW: navigator.serviceWorker.ready
Page->>PS: pushManager.subscribe({ applicationServerKey })
PS-->>Page: PushSubscription (endpoint + keys)
Page->>Srv: POST /subscribe (subscription JSON)
Note over Srv: Stores subscription in DB
Srv->>PS: POST endpoint with encrypted payload + VAPID auth
PS->>SW: push event with data
SW->>SW: event.waitUntil(showNotification(...)) ขั้นตอนที่ 1 — สร้าง VAPID keys
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 1 — สร้าง VAPID keys”VAPID (Voluntary Application Server Identification) keys ใช้ยืนยันตัวตน server ของคุณกับ push service สร้างครั้งเดียวและเก็บอย่างปลอดภัย:
# Using the web-push npm packagenpx web-push generate-vapid-keysซึ่งจะให้ public key (แชร์กับ browser) และ private key (เก็บไว้บน server เท่านั้น — อย่าเปิดเผย)
ขั้นตอนที่ 2 — Subscribe จากหน้าเพจ
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 2 — Subscribe จากหน้าเพจ”// main.js — page codeasync function subscribeToPush() { const registration = await navigator.serviceWorker.ready;
// Convert your VAPID public key (base64url) to a Uint8Array const applicationServerKey = urlBase64ToUint8Array(VAPID_PUBLIC_KEY);
const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, // required: every push must show a notification applicationServerKey, });
// Send the subscription object to your backend await fetch('/api/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription), });
console.log('Subscribed:', subscription.endpoint);}
function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); const rawData = atob(base64); return Uint8Array.from([...rawData].map((c) => c.charCodeAt(0)));}userVisibleOnly: true เป็นข้อบังคับ — browser กำหนดให้ push message ทุกรายการต้องแสดง notification ที่มองเห็นได้
ขั้นตอนที่ 3 — เก็บ subscription บน server ของคุณ
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 3 — เก็บ subscription บน server ของคุณ”Server ของคุณรับ subscription JSON (ที่มี endpoint, keys.auth, keys.p256dh) และเก็บไว้ใน database โดยใช้ user เป็น key
ขั้นตอนที่ 4 — ส่ง push จาก server ของคุณ
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 4 — ส่ง push จาก server ของคุณ”// server.js (Node.js with web-push)import webpush from 'web-push';
webpush.setVapidDetails( process.env.VAPID_PUBLIC_KEY, process.env.VAPID_PRIVATE_KEY,);
async function sendPush(subscription, payload) { await webpush.sendNotification(subscription, JSON.stringify(payload));}
// ExamplesendPush(storedSubscription, { title: 'Your order shipped!', body: 'It will arrive by Thursday.',});ขั้นตอนที่ 5 — จัดการ push event ใน service worker
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 5 — จัดการ push event ใน service worker”// sw.js — service workerself.addEventListener('push', (event) => { const data = event.data ? event.data.json() : { title: 'Update', body: '' };
event.waitUntil( self.registration.showNotification(data.title, { body: data.body, icon: '/icons/icon-192.png', badge: '/icons/badge-72.png', data, }) );});event.waitUntil() ทำให้ SW ยังคงทำงานต่อจนกว่า notification จะถูกแสดง ถ้า SW terminate ก่อนที่ showNotification จะ resolve notification อาจสูญหายได้
การตรวจสอบ subscription ที่มีอยู่
หัวข้อที่มีชื่อว่า “การตรวจสอบ subscription ที่มีอยู่”ก่อน subscribe ให้ตรวจสอบว่ามี subscription อยู่แล้วหรือไม่เพื่อหลีกเลี่ยงการซ้ำกัน:
const existing = await registration.pushManager.getSubscription();if (existing) { console.log('Already subscribed:', existing.endpoint); return existing;}การ Unsubscribe
หัวข้อที่มีชื่อว่า “การ Unsubscribe”const subscription = await registration.pushManager.getSubscription();if (subscription) { await subscription.unsubscribe(); // Also notify your server to remove the stored subscription await fetch('/api/unsubscribe', { method: 'POST', body: JSON.stringify({ endpoint: subscription.endpoint }), });}บทเรียนแบบ code-only: Push ต้องการ backend server ที่มี VAPID keys, database และ push service ตัวอย่างด้านบนสมบูรณ์และถูกต้อง แต่ไม่สามารถรันใน in-browser playground ได้ คัดลอกลงในโปรเจ็กต์จริง เช่น Node.js server ที่มี
web-pushและ frontend framework ใดก็ได้
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| Push API (re-engage ผู้ใช้ที่ไม่ได้เปิดแอป) | ดึงผู้ใช้กลับมาได้แม้ปิดแอปไปนาน เพิ่ม retention | ต้องมี push server/VAPID key และจัดการ subscription lifecycle เอง |
| ไม่ใช้ push เลย พึ่งแค่ในแอป | ไม่ต้องดูแล infrastructure ฝั่ง push server เลย | เสียโอกาส re-engage ผู้ใช้ที่ไม่ได้เปิดแอปเป็นระยะ |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ไม่ handle subscription expiry/rotation ทำให้ push ที่ส่งไปเงียบหายเพราะ endpoint เก่าใช้ไม่ได้แล้ว
- ส่ง push notification บ่อยเกินไปจนผู้ใช้ revoke permission ทิ้งทั้งหมด
- ไม่ encrypt payload ตามสเปก Web Push (VAPID + payload encryption) ทำให้ push provider บาง raw ปฏิเสธ request
💡 ตัวอย่างจากของจริง
Facebook web push — ใช้ Push API แจ้งเตือนข้อความและ activity ให้ผู้ใช้กลับมาที่เว็บแม้ปิด tab ไปแล้ว
สายการบิน (check-in reminder) — ส่ง push แจ้งเตือนเวลา check-in ล่วงหน้า ทำให้ผู้ใช้กลับเข้าแอปโดยไม่ต้องเปิดเองมาเช็ค