Guide du dépôt du SDK JavaScript
À propos du SDK JavaScript de Braze
Le SDK JavaScript de Braze vous aide à intégrer les fonctionnalités de communication, d’analyse et d’engagement des utilisateurs de Braze dans votre application.
Pour commencer, consultez les ressources suivantes :
Aperçu de l’architecture
Le SDK JavaScript de Braze est une bibliothèque indépendante de la plateforme, conçue pour fonctionner dans n’importe quel environnement JavaScript pur. Il ne contient pas d’API propres au navigateur ou à Node.js, ce qui permet de l’utiliser dans divers environnements d’exécution JavaScript.
Principes de conception clés :
- Injection de dépendances : Le SDK nécessite des implémentations pour le stockage, le réseau et les informations sur l’appareil, au lieu d’utiliser des API propres à une plateforme.
- Asynchrone avant tout : La plupart des méthodes de l’API sont asynchrones et renvoient des Promises ; certaines méthodes utilitaires (par exemple
destroy,subscribeToInAppMessage,toggleLogging,setLogger) sont synchrones. Consultez les définitions TypeScript pour connaître les signatures exactes. - Session unique : L’API au niveau du module (
initialize/destroy) gère une seule session SDK active à la fois. - Gestion interne des dépendances : Le SDK crée et gère des dépendances internes (UserManager, SessionManager, DataFlushController, etc.) à partir des implémentations fournies.
Démarrage rapide
Installez le SDK avec npm :
npm install @braze/javascript-sdk
Ou avec yarn :
yarn add @braze/javascript-sdk
Avec l’API au niveau du module :
import { initialize, openSession, changeUser } from '@braze/javascript-sdk';
await initialize({
apiKey,
baseUrl,
options,
sdkMetadata,
deviceInfo,
storageManager,
networkManager,
});
await changeUser(userId);
await openSession();
Conditions préalables
Avant d’intégrer le SDK JavaScript de Braze, vous aurez besoin des éléments suivants :
- Compte Braze : un compte Braze disposant d’un accès à l’API
- Clé API : la clé API de votre application, disponible dans le tableau de bord de Braze
- Endpoint SDK : l’URL de votre endpoint SDK Braze (par exemple,
sdk.iad-01.braze.com)
Obtenir vos identifiants
- Clé API : dans votre tableau de bord de Braze, sous Paramètres → Clés API
- Endpoint SDK : dans Paramètres → Authentification SDK → Endpoints
Intégration
Appeler l’API
Utilisez l’API au niveau du module : appelez initialize() une seule fois, puis appelez les fonctions exportées. Pour changer de configuration, appelez d’abord destroy(), puis à nouveau initialize().
import { initialize, logPurchase, changeUser } from '@braze/javascript-sdk';
await initialize({ apiKey, baseUrl, options, ... });
await changeUser('user-123');
await logPurchase('sku-1', 9.99, 'USD', 1);
Concepts fondamentaux
Implémentations requises
L’objet de configuration d’initialisation requiert storageManager. networkManager et pushManager sont facultatifs.
1. StorageManager - Interface de stockage clé-valeur asynchrone
interface StorageManager {
store(key: string, value: string, isId?: boolean): Promise<void>;
remove(key: string, isId?: boolean): Promise<void>;
retrieve(key: string, isId?: boolean): Promise<string | null>;
clearData(storageKeys: string[]): Promise<void>;
}
- Le paramètre
isIdindique un stockage d’ID persistant : lorsqu’il vauttrue, le SDK stocke un identifiant persistant (ID d’appareil, ID utilisateur) ou l’indicateur de désabonnement. Les implémentations doivent conserver ces valeurs entre les redémarrages de l’application afin que le SDK puisse reconnaître le même appareil ou le même utilisateur. Lorsqu’il vautfalse, la valeur correspond à des données de session ou de cache (événements, attributs, etc.) et peut être stockée uniquement en mémoire. Dans les environnements web, envisagez d’utiliser des cookies pour les clés stockées avecisId: trueafin de garantir leur persistance d’une session à l’autre. - Doit gérer de manière asynchrone toutes les opérations de stockage
2. NetworkManager (facultatif) - Interface de requête HTTP POST
interface NetworkManager {
postRequest(
url: string,
data: Partial<Record<string, unknown>>,
headers?: globalThis.Headers | [string, string][]
): Promise<Partial<Record<string, unknown>>>;
}
- L’implémentation par défaut utilise l’API
fetch(nécessitefetchetURLglobaux) - Peut être remplacée si
fetchn’est pas l’API privilégiée - Remarque : le SDK intègre déjà une logique de nouvelle tentative et de limitation du débit
3. PushManager (facultatif) - Interface de notification push
interface PushManager {
isPushBlocked(): boolean | undefined;
isPushPermissionGranted(): boolean | undefined;
isPushSupported(): boolean | undefined;
registerPush(
successCallback?: (endpoint: string, publicKey: string, userAuth: string) => void,
deniedCallback?: (temporaryDenial: boolean) => void,
): void;
unregisterPush(successCallback?: () => void, errorCallback?: () => void): void;
}
- Requis uniquement si vous mettez en place des notifications push
Vidage des données
Le SDK vide automatiquement les données en cache vers les serveurs de Braze toutes les 10 secondes (configurable via flushIntervalInSeconds). Utilisez requestImmediateDataFlush() pour forcer une synchronisation immédiate.
Modèles d’intégration
Pour les signatures de méthodes, les types de paramètres et de retour ainsi que le détail complet de l’API, consultez les définitions TypeScript du paquet.
Intégration de base
Exemple fonctionnel complet avec gestion des erreurs :
import {
initialize,
openSession,
changeUser,
logCustomEvent,
type StorageManager,
type DeviceInfo
} from '@braze/javascript-sdk';
// Implement required StorageManager interface (in-memory only; does not persist data).
// This example treats all keys equally and ignores the isId parameter
// See "Complete StorageManager implementation with IndexedDB" below for an
// example where we properly handle isId
class InMemoryStorageManager implements StorageManager {
private storage = new Map<string, string>();
async store(key: string, value: string, isId?: boolean): Promise<void> {
this.storage.set(key, value);
}
async retrieve(key: string, isId?: boolean): Promise<string | null> {
return this.storage.get(key) ?? null;
}
async remove(key: string, isId?: boolean): Promise<void> {
this.storage.delete(key);
}
async clearData(storageKeys: string[]): Promise<void> {
for (const key of storageKeys) {
this.storage.delete(key);
}
}
}
const storageManager: StorageManager = new InMemoryStorageManager();
// Provide device information (use your platform's APIs for non-browser)
const deviceInfo: DeviceInfo = {
os: 'my-runtime-os',
language: 'en',
timezone: 'UTC',
browser: 'Chrome', // Optional
browserVersion: '120', // Optional
userAgent: "some-user-agent" // Optional
};
// Browser-only example (uncomment and adapt if you are running in a web browser)
// const deviceInfo: DeviceInfo = {
// os: navigator.platform || 'Unknown',
// language: navigator.language || 'en',
// timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
// browser: 'Chrome', // Optional
// browserVersion: '120', // Optional
// userAgent: navigator.userAgent // Optional
// };
// Initialize SDK
try {
const initialized = await initialize({
apiKey: 'YOUR-API-KEY-HERE',
baseUrl: 'sdk.iad-01.braze.com', // Your Braze SDK endpoint
options: {
sdkVersion: '1.0.0',
enableLogging: true, // Remove in production
sessionTimeoutInSeconds: 1800, // 30 minutes
flushIntervalInSeconds: 10
},
sdkMetadata: ['npm'], // Identify your platform
deviceInfo,
storageManager
});
if (!initialized) {
console.error('Failed to initialize Braze SDK');
return;
}
// Identify user (wait for promise to resolve)
await changeUser('user-123');
// Open session (must be after changeUser)
const isNewSession = await openSession();
console.log('Session opened:', isNewSession ? 'new' : 'resumed');
// Log events
await logCustomEvent('app_opened', {
source: 'homepage',
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Braze SDK error:', error);
}
Implémentation de stockage personnalisée
Implémentation complète de StorageManager avec IndexedDB pour les ID persistants :
import type { StorageManager } from '@braze/javascript-sdk';
class IndexedDBStorageManager implements StorageManager {
private dbName = 'braze-storage';
private storeName = 'braze-ids';
private memoryCache = new Map<string, string>();
private db: IDBDatabase | null = null;
private dbInitPromise: Promise<void> | null = null;
private async initDB(): Promise<void> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
});
}
private async ensureDB(): Promise<void> {
if (this.dbInitPromise !== null) {
return this.dbInitPromise;
}
this.dbInitPromise = this.initDB();
return this.dbInitPromise;
}
async store(key: string, value: string, isId?: boolean): Promise<void> {
await this.ensureDB();
this.memoryCache.set(key, value);
if (isId && this.db) {
try {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
await new Promise<void>((resolve, reject) => {
const request = store.put(value, key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
} catch (error) {
console.error('Failed to store ID in IndexedDB:', error);
}
}
}
async retrieve(key: string, isId?: boolean): Promise<string | null> {
await this.ensureDB();
if (this.memoryCache.has(key)) {
return this.memoryCache.get(key) || null;
}
if (isId && this.db) {
try {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
return new Promise<string | null>((resolve, reject) => {
const request = store.get(key);
request.onsuccess = () => {
const value = request.result;
if (value) {
this.memoryCache.set(key, value);
}
resolve(value || null);
};
request.onerror = () => reject(request.error);
});
} catch (error) {
console.error('Failed to retrieve ID from IndexedDB:', error);
return null;
}
}
return null;
}
async remove(key: string, isId?: boolean): Promise<void> {
await this.ensureDB();
this.memoryCache.delete(key);
if (isId && this.db) {
try {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
await new Promise<void>((resolve, reject) => {
const request = store.delete(key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
} catch (error) {
console.error('Failed to remove ID from IndexedDB:', error);
}
}
}
async clearData(storageKeys: string[]): Promise<void> {
await this.ensureDB();
for (const key of storageKeys) {
this.memoryCache.delete(key);
}
if (this.db) {
try {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
await Promise.all(
storageKeys.map(
(key) =>
new Promise<void>((resolve, reject) => {
const request = store.delete(key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
})
)
);
} catch (error) {
console.error('Failed to clear data from IndexedDB:', error);
}
}
}
}
const storageManager = new IndexedDBStorageManager();
Implémentation réseau personnalisée
NetworkManager qui enregistre chaque requête sortante (le SDK gère déjà les erreurs et les nouvelles tentatives) :
import type { NetworkManager } from '@braze/javascript-sdk';
function logRequest(url: string, data: Partial<Record<string, unknown>>): void {
// Send to your analytics, monitoring, or logging backend
console.log('Braze SDK request', { url, data });
}
class LoggingNetworkManager implements NetworkManager {
async postRequest(
url: string,
data: Partial<Record<string, unknown>>,
headers?: Headers | [string, string][]
): Promise<Partial<Record<string, unknown>>> {
logRequest(url, data);
const requestHeaders = new Headers(headers);
requestHeaders.set('Content-Type', 'application/json');
const response = await fetch(url, {
method: 'POST',
headers: requestHeaders,
body: JSON.stringify(data),
});
const result = await response.json();
return result as Partial<Record<string, unknown>>;
}
}
const networkManager = new LoggingNetworkManager();
Gestion des erreurs
Modèles complets de gestion des erreurs :
import {
getUserId,
logCustomEvent,
initialize,
} from '@braze/javascript-sdk';
// Pattern 1: Check for undefined (SDK not initialized)
async function getDevice() {
const deviceId = await getDeviceId();
if (deviceId === undefined) {
console.warn('SDK not initialized');
return null;
}
return deviceId;
}
// Pattern 2: Try-catch for methods that may throw
async function logEventSafely() {
try {
const success = await logCustomEvent('button_clicked', { button: 'submit' });
if (success === undefined) {
console.warn('SDK not initialized, event not logged');
} else if (success) {
console.log('Event logged successfully');
} else {
console.warn('Event failed to enqueue');
}
} catch (error) {
console.error('Error logging event:', error);
// Handle error (e.g., retry, queue for later)
}
}
// Pattern 3: Handle null vs undefined distinction
async function checkUser() {
const userId = await getUserId();
if (userId === undefined) {
// SDK not initialized
console.warn('SDK not initialized');
} else if (userId === null) {
// Current user is anonymous
console.log('Current user is anonymous');
} else {
// User is identified
console.log(`User ID is ${userId}`);
}
}
// Pattern 4: Handle initialization errors
async function initializeSafely() {
try {
const initialized = await initialize({
apiKey: 'YOUR-API-KEY',
baseUrl: 'sdk.iad-01.braze.com',
options: { sdkVersion: '1.0.0' },
sdkMetadata: ['npm'],
deviceInfo: { os: 'iOS', language: 'en', timezone: 'UTC' },
storageManager: myStorageManager
});
if (!initialized) {
console.error('Failed to initialize SDK');
// Check if already initialized, disabled, or validation failed
return false;
}
return true;
} catch (error) {
console.error('Initialization error:', error);
return false;
}
}
Gestion des abonnements
import {
ControlMessage,
logInAppMessageImpression,
removeSubscription,
subscribeToInAppMessage,
} from '@braze/javascript-sdk';
const displayMessage = (inAppMessage) => {
// Add custom code to display in-app messages
}
// Subscribe to in-app messages
const subscriptionId = subscribeToInAppMessage((inAppMessage) => {
if (inAppMessage instanceof ControlMessage) {
return; // Skip control messages
}
displayMessage(inAppMessage);
logInAppMessageImpression(inAppMessage);
});
// Later, remove subscription if it was successfully created
if (subscriptionId) {
removeSubscription(subscriptionId);
}
Changer de configuration : une seule session active peut exister à la fois. Pour changer de configuration, appelez destroy() puis initialize() :
import { destroy, initialize } from '@braze/javascript-sdk';
destroy();
await initialize({ /* new config */ });
Cas d’usage courants
Identification des utilisateurs et suivi des attributs
import {
changeUser,
setCustomUserAttribute,
setUserEmail,
setUserFirstName,
setUserLastName,
} from '@braze/javascript-sdk';
// Identify user
await changeUser('user-123');
// Set standard attributes
await setUserEmail('[email protected]');
await setUserFirstName('John');
await setUserLastName('Doe');
// Set custom attributes
await setCustomUserAttribute('subscription_tier', 'premium');
await setCustomUserAttribute('last_login', new Date());
await setCustomUserAttribute('tags', ['vip', 'early-adopter']);
Enregistrement des événements et analyse
import {
logCustomEvent,
logPurchase,
requestImmediateDataFlush,
} from '@braze/javascript-sdk';
await logCustomEvent('product_viewed', {
product_id: '123',
category: 'electronics',
price: 99.99
});
await logPurchase('product-123', 99.99, 'USD', 1, {
category: 'electronics'
});
// Flushing these events to the server will happen periodically,
// however you can manually trigger a flush if necessary
requestImmediateDataFlush((success) => {
console.log('Data flushed:', success);
});
Gestion des messages in-app
import {
ControlMessage,
logInAppMessageImpression,
subscribeToInAppMessage,
} from '@braze/javascript-sdk';
const displayInAppMessage = async (inAppMessage) => {
// Add custom code to display in-app messages
}
subscribeToInAppMessage(async (inAppMessage) => {
if (inAppMessage instanceof ControlMessage) {
return;
}
await displayInAppMessage(inAppMessage);
await logInAppMessageImpression(inAppMessage);
});
Gestion des erreurs et cas limites
Conditions d’erreur courantes
SDK non initialisé :
- La plupart des méthodes renvoient
undefined(sans lever d’exception) lorsque le SDK n’est pas initialisé initialize()renvoiefalsesi le SDK est déjà initialisé ou si la validation échouechangeUser()n’a aucun effet et la promesse est résolue si le SDK n’est pas initialisé- Vérifiez toujours les valeurs de retour
undefinedavant de les utiliser
Échecs de validation :
- Clé API ou URL de base non valide :
initialize()renvoiefalseet enregistre une erreur - Noms ou clés d’événement non valides : 255 caractères maximum, ne peuvent pas commencer par
$, caractères alphanumériques et ponctuation uniquement - Valeurs d’attribut non valides : chaînes de caractères de 255 caractères maximum, sans sauts de ligne, tabulations ni guillemets doubles, ne pouvant pas commencer par
$ - Codes de devise non valides : les codes non pris en charge génèrent un avertissement et aucune action n’est effectuée
- Quantité d’achat non valide : doit être comprise entre 1 et 100, sinon elle est ignorée
Erreurs réseau :
- La méthode
postRequest()de NetworkManager doit gérer les erreurs et rejeter les promesses de manière appropriée - Le contrôleur de vidage des données relance automatiquement les requêtes ayant échoué
- Utilisez le rappel de
requestImmediateDataFlush()pour détecter les échecs de vidage
Erreurs de stockage :
- Les méthodes de StorageManager doivent gérer les erreurs de manière élégante
- Si le stockage échoue, le SDK risque de ne pas fonctionner correctement
- L’indicateur
isIddétermine la persistance : les ID persistent d’une session à l’autre, tandis que les objets sont limités à la session
Cas limites liés à l’identification des utilisateurs :
- Impossible de revenir à un utilisateur anonyme après l’identification
- Un changement d’utilisateur met fin à la session en cours et en démarre une nouvelle
- L’historique de l’utilisateur anonyme est conservé lors de la première identification
- L’historique est fusionné si l’utilisateur existe sur un autre appareil
Gestion des sessions :
- Les sessions expirent après 30 minutes d’inactivité (configurable)
openSession()renvoietruepour une nouvelle session etfalsesi la session est reprise- Vous devez appeler
openSession()aprèschangeUser()ousetIdentifierToken()
Gestion des abonnements :
- Les rappels d’abonnement sont appelés de manière synchrone lorsque des événements se produisent
- Supprimez les abonnements pour éviter les fuites de mémoire
removeAllSubscriptions()supprime tous les abonnements en une seule fois
Vidage des données :
- Vidage automatique toutes les 10 secondes (configurable, minimum : 3 secondes)
- Le vidage peut échouer silencieusement : utilisez le rappel de
requestImmediateDataFlush() - Les données sont mises en file d’attente si le réseau n’est pas disponible, puis vidées lorsque le réseau est rétabli
Remarques importantes sur le déploiement
-
La plupart des méthodes sont asynchrones : les méthodes asynchrones du SDK renvoient une promesse (utilisez
awaitou.then()). Certaines méthodes de configuration et utilitaires (par exemple,destroy,toggleLogging,setLogger) sont synchrones ; consultez les définitions TypeScript ou le tableau de référence rapide pour plus de détails. -
Les méthodes peuvent renvoyer
undefined: si le SDK n’est pas initialisé, la plupart des méthodes renvoientundefinedau lieu de lever une exception. Vérifiez la présence deundefinedavant d’utiliser les valeurs de retour. -
Les méthodes peuvent renvoyer
null: certaines méthodes renvoientnullpour indiquer « introuvable » (par exemple,getUserId()renvoienullsi l’utilisateur est anonyme). Cette valeur est différente deundefined(SDK non initialisé). - Les clés de stockage utilisent l’indicateur
isId: le paramètreisIddes méthodes de StorageManager distingue :- Le stockage d’ID : identifiants persistants (ID d’appareil, ID utilisateur) qui doivent persister d’une session à l’autre
- Le stockage d’objets : données limitées à la session, qui peuvent être effacées
-
Tags de métadonnées du SDK : le tableau
sdkMetadataidentifie la plateforme ou le wrapper qui utilise le SDK (par exemple,['npm']ou[BrazeSdkMetadata.NPM]). Les tags valides sont définis par l’enumBrazeSdkMetadata(commenpm,cdn,manu,shp,gg,kep), et le SDK ajoute automatiquement'wjs'pour indiquer le SDK JavaScript. -
NetworkManager par défaut : si
networkManagern’est pas fourni, le SDK utilise une implémentation par défaut qui nécessite les API globalesfetchetURL. Fournissez une implémentation personnalisée si celles-ci ne sont pas disponibles. -
PushManager est facultatif : n’implémentez
PushManagerque si vous avez besoin de la fonctionnalité de notification push. Il peut être omis dans le cas contraire. -
Destruction et nettoyage : appelez
destroy()lorsque vous devez arrêter le SDK. Une seule session active peut exister à la fois ; vous devez appelerdestroy()avant de rappelerinitialize(). Cette opération arrête les minuteurs, vide les données et libère les ressources. -
Vidage des données : les données sont automatiquement vidées toutes les 10 secondes (configurable). Utilisez
requestImmediateDataFlush()pour une synchronisation immédiate. -
Gestion des sessions : appelez toujours
openSession()aprèschangeUser()ousetIdentifierToken()pour éviter de créer des utilisateurs anonymes en double. -
Sécurité des types : le SDK est écrit en TypeScript et fournit des définitions de types complètes. Utilisez TypeScript pour profiter de la meilleure expérience et de la vérification des types.
- Règles de validation : les noms d’événements, les clés d’attributs et les clés de propriétés sont soumis à une validation stricte (255 caractères maximum, ne peuvent pas commencer par
$, caractères alphanumériques et ponctuation uniquement). Les valeurs non valides peuvent être ignorées ou provoquer des erreurs.
Débogage / Résolution des problèmes
Ajoutez l’option enableLogging: true aux options d’initialisation. Elle est très utile pendant le développement, mais veillez à la supprimer ou à fournir un autre enregistreur avant de mettre votre page en production.
Contact
Si vous avez des questions, veuillez contacter [email protected].
Pour les détails du dépôt et les exemples de projets, consultez https://github.com/braze-inc/braze-javascript-sdk.