はじめに
こんにちは、エンジニアのkeitaMaxです。
firebaseからNext.jsのプロジェクトに向けてPushを送ってみようと思います。
基本的にこの記事を元に行っていこうと思います
firebaseの設定
まうfirebaseに新しいプロジェクトを作成してAPIKeyなどを取得するようにします。
プロジェクトの作成
アプリの登録
この画面でapiKeyなどが出てくるのでメモっておきましょう
Keyの作成
プロジェクトの設定>Cloud Messaging>Generate key pair
インストール
以下でfirebaseのライブラリをインストールします。
npm install firebase
コード
基本的には参考元と同様にしますが、同じだとESLintでエラーがでていたので、少し修正しています。
"use client"
import { initializeApp } from 'firebase/app'
import { getMessaging } from 'firebase/messaging'
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
}
const app = initializeApp(firebaseConfig)
let messaging: ReturnType<typeof getMessaging> | null = null
if (typeof window !== 'undefined') {
try {
messaging = getMessaging(app)
} catch (error) {
console.error('Firebase Messaging初期化エラー:', error)
}
}
export { messaging }
"use client"
import { useEffect, useState } from "react"
import { MessagePayload, onMessage } from "firebase/messaging"
import { messaging } from "~/firebase/config"
import useFCMToken from "./useFCMToken"
export const useFCM = () => {
const fcmToken = useFCMToken()
const [messages, setMessages] = useState<MessagePayload[]>([])
useEffect(() => {
if (typeof window === 'undefined') return
if (!("serviceWorker" in navigator)) return
if (!messaging) return
try {
const unsubscribe = onMessage(messaging, (payload) => {
setMessages((messages) => [...messages, payload])
})
return () => unsubscribe()
} catch (error) {
console.error('FCM初期化エラー:', error)
}
}, [fcmToken])
return { fcmToken, messages }
}
"use client"
import { useEffect, useState } from "react"
import { getToken, isSupported } from "firebase/messaging"
import { messaging } from "~/firebase/config"
import useNotificationPermissionStatus from "./useNotificationPermissionStatus"
const useFCMToken = () => {
const permission = useNotificationPermissionStatus()
const [fcmToken, setFcmToken] = useState<string | null>(null)
useEffect(() => {
const retrieveToken = async () => {
if (typeof window === "undefined") return
if (!("serviceWorker" in navigator)) return
if (permission !== "granted") return
if (!messaging) return
try {
const isFCMSupported = await isSupported()
if (!isFCMSupported) return
// Service Workerの登録
await navigator.serviceWorker.register('/web-push/firebase-messaging-sw.js', {
scope: '/web-push/'
})
const token = await getToken(messaging, {
vapidKey: process.env.NEXT_PUBLIC_FIREBASE_VAPID_KEY,
serviceWorkerRegistration: await navigator.serviceWorker.getRegistration()
})
setFcmToken(token)
} catch (error) {
console.error('FCMトークン取得エラー:', error)
}
}
retrieveToken()
}, [permission])
return fcmToken
}
export default useFCMToken
"use client"
import { useEffect, useState } from "react"
type NotificationPermission = "default" | "granted" | "denied"
// type PermissionName = "notifications"
const useNotificationPermissionStatus = () => {
const [permission, setPermission] = useState<NotificationPermission>("default")
useEffect(() => {
if (typeof window === "undefined") return
if (!("Notification" in window)) return
const handler = () => setPermission(Notification.permission)
handler()
try {
Notification.requestPermission().then(handler)
navigator.permissions
.query({ name: "notifications" })
.then((notificationPerm) => {
notificationPerm.onchange = handler
})
.catch((error) => {
console.error('通知許可状態の取得エラー:', error)
})
} catch (error) {
console.error('通知許可のリクエストエラー:', error)
}
}, [])
return permission
}
export default useNotificationPermissionStatus
NEXT_PUBLIC_FIREBASE_APP_ID=
NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
NEXT_PUBLIC_FIREBASE_VAPID_KEY=
// eslint-disable-next-line no-undef
importScripts("https://www.gstatic.com/firebasejs/8.8.0/firebase-app.js")
// eslint-disable-next-line no-undef
importScripts("https://www.gstatic.com/firebasejs/8.8.0/firebase-messaging.js")
// eslint-disable-next-line no-undef
const firebaseConfig = {
apiKey: "AIzaSyBblSP-",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
}
// eslint-disable-next-line no-undef
firebase.initializeApp(firebaseConfig)
// eslint-disable-next-line no-undef
const messaging = firebase.messaging()
messaging.onBackgroundMessage((payload) => {
console.log(
"[firebase-messaging-sw.js] Received background message ",
payload
)
const notificationTitle = payload.notification.title
const notificationOptions = {
body: payload.notification.body,
icon: "/web-push/logo.png",
}
self.registration.showNotification(notificationTitle, notificationOptions)
})
firebase-messaging-sw.js
はGitには上げていません。
以下のようにGitHubActionsでbuildする時に作成するようにしました。
name: Deploy to GitHub Pages
# on: [pull_request]
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: 'npm'
- name: Copy .env.example
run: cp .env.example .env
- name: Inject Firebase secrets into .env
run: |
echo "NEXT_PUBLIC_FIREBASE_API_KEY=${{ secrets.NEXT_PUBLIC_FIREBASE_API_KEY }}" >> .env
echo "NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=${{ secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN }}" >> .env
echo "NEXT_PUBLIC_FIREBASE_PROJECT_ID=${{ secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID }}" >> .env
echo "NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=${{ secrets.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET }}" >> .env
echo "NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }}" >> .env
echo "NEXT_PUBLIC_FIREBASE_APP_ID=${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }}" >> .env
echo "NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=${{ secrets.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID }}" >> .env
echo "NEXT_PUBLIC_FIREBASE_VAPID_KEY=${{ secrets.NEXT_PUBLIC_FIREBASE_VAPID_KEY }}" >> .env
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./out
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
テスト
実際にテストでPushを送ってみましょう。
npm run dev
をしてNext.jsのアプリケーションを起動させておきます
firebase上でPushを作成します。
これでメッセージの準備はできました。
次にNextjs側のトークンを取得します。
npm run dev すると出てくるトークンをコピーして入力します。
これをfirebaseの方に入力します。
この状態でテストとすると、Push通知が送られてきて
このような感じでメッセージが表示されます
おわりに
この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。
最後まで読んでいただきありがとうございました!
参考