À propos du SDK JavaScript de Braze
Le SDK JavaScript de Braze vous aide à intégrer les fonctionnalités d’envoi de messages, d’analyse et d’engagement utilisateur 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 tout environnement JavaScript pur. Il ne contient pas d’API spécifiques au navigateur ou à Node.js, ce qui le rend adapté à une utilisation 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 plutôt que d’utiliser des API spécifiques à la plateforme
- Asynchrone d’abord : la plupart des méthodes de l’API sont asynchrones et retournent des Promises ; certaines méthodes utilitaires (par exemple
destroy,subscribeToInAppMessage,toggleLogging,setLogger) sont synchrones. Consultez les définitions TypeScript pour les signatures exactes. - Session singleton : l’API au niveau du module (
initialize/destroy) gère une seule session SDK active à la fois. - Gestion interne des dépendances : crée et gère les dépendances internes (UserManager, SessionManager, DataFlushController, etc.) à partir des implémentations fournies
Démarrage rapide
Installez le SDK avec npm :
1
npm install @braze/javascript-sdk
Ou avec yarn :
1
yarn add @braze/javascript-sdk
Avec l’API au niveau du module :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 de :
- Compte Braze : un compte Braze avec accès à l’API
- Clé API : la clé API de votre application depuis 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 : disponible dans votre tableau de bord de Braze sous Paramètres > Clés API
- Endpoint SDK : situé dans Paramètres > Authentification SDK > Endpoints
Intégration
Appeler l’API
Utilisez l’API au niveau du module : appelez initialize() une fois, puis appelez les fonctions exportées. Pour changer de configuration, appelez d’abord destroy(), puis initialize() à nouveau.
1
2
3
4
5
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 nécessite storageManager. networkManager et pushManager sont facultatifs.
1. StorageManager - Interface de stockage clé-valeur asynchrone
1
2
3
4
5
6
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’identifiant persistant : lorsqu’il esttrue, le SDK stocke un identifiant persistant (ID d’appareil, ID utilisateur) ou le drapeau de désinscription. Les implémentations doivent persister ces données entre les redémarrages de l’application afin que le SDK puisse reconnaître le même appareil/utilisateur. Lorsqu’il estfalse, la valeur est une donnée de session/cache (événements, attributs, etc.) et peut être uniquement en mémoire. Pour les environnements web, envisagez d’utiliser des cookies pour les clés stockées avecisId: trueafin d’assurer la persistance entre les sessions. - Doit gérer les opérations asynchrones pour toutes les opérations de stockage
2. NetworkManager (facultatif) - Interface de requête HTTP POST
1
2
3
4
5
6
7
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 préférée - Remarque : le SDK intègre déjà une logique de réessai et de limitation de débit
3. PushManager (facultatif) - Interface de notification push
1
2
3
4
5
6
7
8
9
10
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 implémentez les notifications push
Vidage des données
Le SDK vide automatiquement les données en cache vers les serveurs 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, et les détails complets de l’API, consultez les définitions TypeScript dans le package.
Intégration de base
Exemple fonctionnel complet avec gestion des erreurs :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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 identifiants persistants :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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 journalise chaque requête sortante (le SDK gère déjà les erreurs et les réessais) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);
}
Changement de configuration : une seule session active existe à la fois. Pour changer de configuration, appelez destroy() puis initialize() :
1
2
3
4
import { destroy, initialize } from '@braze/javascript-sdk';
destroy();
await initialize({ /* new config */ });
Cas d’utilisation courants
Identification des utilisateurs et suivi des attributs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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']);
Journalisation des événements et analyse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 retournent
undefined(sans lever d’exception) lorsque le SDK n’est pas initialisé initialize()retournefalses’il est déjà initialisé ou si la validation échouechangeUser()est un no-op et la promesse se résout 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 invalide :
initialize()retournefalse, journalise l’erreur - Noms d’événements/clés invalides : maximum 255 caractères, ne peuvent pas commencer par
$, alphanumériques + ponctuation uniquement - Valeurs d’attributs invalides : chaînes de 255 caractères maximum, pas de retours à la ligne/tabulations/guillemets doubles, ne peuvent pas commencer par
$ - Codes de devise invalides : les codes non pris en charge génèrent un avertissement, aucune action effectuée
- Quantité d’achat invalide : doit être entre 1 et 100, sinon ignorée
Erreurs réseau :
postRequest()du NetworkManager doit gérer les erreurs et rejeter les promesses de manière appropriée- Le contrôleur de vidage des données réessaie automatiquement les requêtes échouées
- Utilisez le rappel de
requestImmediateDataFlush()pour détecter les échecs de vidage
Erreurs de stockage :
- Les méthodes du StorageManager doivent gérer les erreurs de manière élégante
- Si le stockage échoue, le SDK peut ne pas fonctionner correctement
- Le drapeau
isIddétermine la persistance : les identifiants persistent entre les sessions, les objets sont limités à la session
Cas limites d’identification des utilisateurs :
- Impossible de revenir à un utilisateur anonyme après identification
- Le changement d’utilisateur met fin à la session en cours et en démarre une nouvelle
- L’historique de l’utilisateur anonyme est préservé 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()retournetruepour une nouvelle session,falsesi elle 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()efface 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 est indisponible, vidées lorsque le réseau est rétabli
Notes importantes sur l’implémentation
-
La plupart des méthodes sont asynchrones : les méthodes asynchrones du SDK retournent une Promise (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 retourner
undefined: si le SDK n’est pas initialisé, la plupart des méthodes retournentundefinedau lieu de lever une exception. Vérifiez la présence deundefinedavant d’utiliser les valeurs de retour. -
Les méthodes peuvent retourner
null: certaines méthodes retournentnullpour indiquer « non trouvé » (par exemple,getUserId()retournenullsi l’utilisateur est anonyme). Cela diffère deundefined(SDK non initialisé). - Les clés de stockage utilisent le drapeau
isId: le paramètreisIddans les méthodes du StorageManager distingue entre :- Stockage d’identifiants : identifiants persistants (ID d’appareil, ID utilisateur) qui doivent persister entre les sessions
- Stockage d’objets : données limitées à la session qui peuvent être effacées
-
Étiquettes de métadonnées SDK : le tableau
sdkMetadataidentifie la plateforme/le wrapper utilisant le SDK (par exemple,['npm']ou[BrazeSdkMetadata.NPM]). Les étiquettes valides sont définies par l’enumBrazeSdkMetadata(telles quenpm,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 démonter le SDK. Une seule session active peut exister à la fois ; vous devez appelerdestroy()avant d’appelerinitialize()à nouveau. Cela arrête les minuteries, 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 avec des définitions de types complètes. Utilisez TypeScript pour une meilleure expérience et une 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 ont une validation stricte (255 caractères maximum, ne peuvent pas commencer par
$, alphanumériques + ponctuation uniquement). Les valeurs invalides peuvent être ignorées ou provoquer des erreurs.
Débogage et résolution des problèmes
Passez l’option enableLogging: true dans les options d’initialisation. Cela est utile pour le développement, mais assurez-vous de supprimer cette option ou de fournir un logger alternatif 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 projets d’exemple, consultez https://github.com/braze-inc/braze-javascript-sdk.