はじめに
実務にて、@aws-sdk/client-secrets-managerライブラリを用いて、
SecretManager + FCM によるプッシュ通知機能
の実装を行いましたので、備忘録として参考ロジックの記載になります。
目次
🪄 使用パッケージ
secretsManager/packages.json
"dependencies": {
"@packages/database": "*",
"@packages/logger": "*",
"@sentry/node": "^8.50.0",
"@sentry/profiling-node": "^8.50.0",
"@types/aws-lambda": "^8.10.147",
"dotenv": "^16.4.7",
"firebase-admin": "^13.4.0",
"iconv-lite": "^0.6.3",
"inversify": "^6.2.1",
"papaparse": "^5.5.2",
"uuid": "^11.1.0"
},
🪄 メインロジック
secretsManager/repository/index.ts
import { GetSecretValueCommand, SecretsManagerClient } from '@aws-sdk/client-secrets-manager';
import { DBConstants, type Repositories } from '@packages/database';
import { loggerError, loggerInfo } from '@packages/logger';
import admin from 'firebase-admin';
import { inject, injectable } from 'inversify';
import type { IPushNotificationUseCase } from './interface';
import { type AnnouncementListPushNotificationRecord, NotificationContent } from './types';
@injectable()
/** プッシュ通知ユースケース */
export class PushNotificationUseCase implements IPushNotificationUseCase {
/**
* インスタンスを生成する
*
* @constructor
* @param announcementRepo - お知らせリポジトリ
*/
public constructor(
@inject(DBConstants.Types.AnnouncementRepository)
private readonly announcementRepo: Repositories.IAnnouncementRepository,
) {}
/**
* お知らせ情報をプッシュ通知する
*
*/
public async pushNotification(): Promise<void> {
loggerInfo('Process Start');
try {
// プッシュ通知対象のお知らせ情報を取得
const pastPublishAnnouncements = await this.announcementRepo.listPushNotificationWithFacility();
if (pastPublishAnnouncements.records.length === 0) {
loggerInfo('pushNotificationUseCase.pushNotification', {
message: 'announcements record not found',
});
return;
}
// Firebase Admin SDK の初期化
await this.initFirebaseAdmin();
// 取得したおしらせ情報ごとにプッシュ通知を実行
for (const record of pastPublishAnnouncements.records) {
await this.executePushNotification(record);
}
} catch (error) {
if (error instanceof Error) {
loggerError('pushNotificationUseCase.pushNotification', {
message: error.message,
});
}
throw error;
}
loggerInfo('Process End');
}
/**
* プッシュ通知を実行する
*
*/
private async executePushNotification(
params: AnnouncementListPushNotificationRecord,
): Promise<{ success: boolean; response?: string }> {
try {
loggerInfo('Start Executing');
// 通知内容の作成
const data = JSON.stringify({
type: NotificationContent.type,
id: params.id,
facility: {
id: params.facilityId,
name: params.facilityName,
logoUrl: params.logoUrl,
},
});
const message: admin.messaging.Message = {
token: params.deviceToken,
notification: {
title: NotificationContent.title,
body: params.title,
},
data: {
data,
},
};
// FCM で通知を送信
const response = await admin.messaging().send(message);
loggerInfo('pushNotificationUseCase.executePushNotification', {
message: 'fcm notification sent',
params,
response,
});
// お知らせ未読情報の登録
await this.announcementRepo.createUnread({
announcementId: params.id,
userId: params.userId,
});
loggerInfo('End Executed');
return {
success: true,
response,
};
} catch (error) {
if (error instanceof Error) {
loggerError('pushNotificationUseCase.executePushNotification', {
message: error.message,
params,
});
}
throw error;
}
}
// Firebase Admin SDK の初期化メソッド
private async initFirebaseAdmin(): Promise<void> {
if (!admin.apps.length) {
const serviceAccount = await this.getFirebaseCredentialsFromSecret();
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
}
}
// Secrets Manager から Firebase サービスアカウントキーを取得
private async getFirebaseCredentialsFromSecret(): Promise<admin.ServiceAccount> {
const client = new SecretsManagerClient({ region: process.env.AWS_REGION });
const command = new GetSecretValueCommand({ SecretId: process.env.FIREBASE_SERVICE_ACCOUNT_SECRET_ID });
const response = await client.send(command);
if (!response.SecretString) {
throw new Error('Secret not found or empty');
}
const secretData = JSON.parse(response.SecretString);
const serviceAccountKey = secretData.RGT_APP_PRIVATE_KEY;
if (!serviceAccountKey) {
throw new Error('Firebase service account key not found in secret');
}
return JSON.parse(serviceAccountKey);
}
}
🪄 インターフェース
secretsManager/repository/interface.ts
export interface IPushNotificationUseCase {
/**
* お知らせ情報をプッシュ通知する
*
*/
pushNotification(): Promise<void>;
}
🪄 まとめ
処理概要
- Firebase Admin SDK の初期化
- Secrets Manager から Firebase サービスアカウントキーを取得
- FCMでプッシュ通知
改良点
-
throw new Error箇所は、ロガーライブラリを用いてカスタム可能なエラークラスを別ファイルで作成・参照する形にすると良い! - さらに、@sentry/nodeを用いてSentry通知可能にするとより実務的なサービス構成になる。