JavaScript SDK – Repository-Leitfaden
Über das Braze JavaScript SDK
Das Braze JavaScript SDK hilft Ihnen, Braze-Messaging-, Analytics- und Nutzer:innen-Engagement-Funktionen in Ihre Anwendung zu integrieren.
Für den Einstieg nutzen Sie die folgenden Ressourcen:
Architektur-Übersicht
Das Braze JavaScript SDK ist eine plattformunabhängige Bibliothek, die in jeder reinen JavaScript-Umgebung funktioniert. Es enthält keine browser- oder Node.js-spezifischen APIs, was es für den Einsatz in verschiedenen JavaScript-Laufzeitumgebungen geeignet macht.
Zentrale Design-Prinzipien:
- Dependency Injection: Das SDK erfordert Implementierungen für Speicher, Netzwerk und Geräteinformationen, anstatt plattformspezifische APIs zu verwenden
- Async-First: Die meisten API-Methoden sind asynchron und geben Promises zurück; einige Hilfsmethoden (z. B.
destroy,subscribeToInAppMessage,toggleLogging,setLogger) sind synchron. Die genauen Signaturen finden Sie in den TypeScript-Definitionen. - Singleton-Session: Die API auf Modulebene (
initialize/destroy) verwaltet jeweils eine aktive SDK-Session. - Internes Dependency-Management: Erstellt und verwaltet interne Abhängigkeiten (UserManager, SessionManager, DataFlushController usw.) auf Basis der bereitgestellten Implementierungen
Schnellstart
Installieren Sie das SDK mit npm:
npm install @braze/javascript-sdk
Oder mit yarn:
yarn add @braze/javascript-sdk
Mit der API auf Modulebene:
import { initialize, openSession, changeUser } from '@braze/javascript-sdk';
await initialize({
apiKey,
baseUrl,
options,
sdkMetadata,
deviceInfo,
storageManager,
networkManager,
});
await changeUser(userId);
await openSession();
Voraussetzungen
Bevor Sie das Braze JavaScript SDK integrieren, benötigen Sie Folgendes:
- Braze-Konto: Ein Braze-Konto mit API-Zugang
- API-Schlüssel: Den API-Schlüssel Ihrer App aus dem Braze-Dashboard
- SDK-Endpunkt: Die URL Ihres Braze SDK-Endpunkts (z. B.
sdk.iad-01.braze.com)
Ihre Zugangsdaten abrufen
- API-Schlüssel: Zu finden in Ihrem Braze-Dashboard unter Einstellungen > API-Schlüssel
- SDK-Endpunkt: Zu finden unter Einstellungen > SDK-Authentifizierung > Endpunkte
Integration
API aufrufen
Verwenden Sie die API auf Modulebene: Rufen Sie initialize() einmal auf und verwenden Sie dann die exportierten Funktionen. Um die Konfiguration zu wechseln, rufen Sie zuerst destroy() und dann erneut initialize() auf.
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);
Kernkonzepte
Erforderliche Implementierungen
Das Konfigurationsobjekt für die Initialisierung erfordert storageManager. networkManager und pushManager sind optional.
1. StorageManager – Asynchrone Schlüssel-Wert-Speicherschnittstelle
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>;
}
- Der Parameter
isIdkennzeichnet persistente ID-Speicherung: Wenntrue, speichert das SDK einen persistenten Bezeichner (Geräte-ID, Nutzer-ID) oder das Opt-out-Flag. Implementierungen sollten diese über App-Neustarts hinweg beibehalten, damit das SDK dasselbe Gerät bzw. dieselbe:n Nutzer:in erkennen kann. Wennfalse, handelt es sich um Sitzungs-/Cache-Daten (Ereignisse, Attribute usw.), die nur im Arbeitsspeicher gehalten werden können. In Web-Umgebungen sollten Sie für Schlüssel, die mitisId: truegespeichert werden, Cookies verwenden, um sitzungsübergreifende Persistenz sicherzustellen. - Muss asynchrone Operationen für alle Speichervorgänge unterstützen
2. NetworkManager (optional) – Schnittstelle für HTTP-POST-Anfragen
interface NetworkManager {
postRequest(
url: string,
data: Partial<Record<string, unknown>>,
headers?: globalThis.Headers | [string, string][]
): Promise<Partial<Record<string, unknown>>>;
}
- Die Standardimplementierung verwendet die
fetch-API (erfordert globalesfetchundURL) - Kann überschrieben werden, wenn
fetchnicht die bevorzugte API ist - Hinweis: Das SDK verfügt bereits über eine integrierte Logik für Wiederholungsversuche und Rate-Limiting
3. PushManager (optional) – Schnittstelle für Push-Benachrichtigungen
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;
}
- Nur erforderlich, wenn Push-Benachrichtigungen implementiert werden
Data-Flushing
Das SDK überträgt zwischengespeicherte Daten automatisch alle 10 Sekunden an die Braze-Server (konfigurierbar über flushIntervalInSeconds). Verwenden Sie requestImmediateDataFlush(), um eine sofortige Synchronisierung zu erzwingen.
Integrationsmuster
Für Methodensignaturen, Parameter- und Rückgabetypen sowie vollständige API-Details siehe die TypeScript-Definitionen im Paket.
Grundlegende Integration
Vollständiges, funktionsfähiges Beispiel mit Fehlerbehandlung:
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);
}
Angepasste Speicher-Implementierung
Vollständige StorageManager-Implementierung mit IndexedDB für persistente IDs:
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();
Angepasste Netzwerk-Implementierung
NetworkManager, der jede ausgehende Anfrage protokolliert (das SDK übernimmt bereits die Fehlerbehandlung und Wiederholungsversuche):
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();
Fehlerbehandlung
Vollständige Muster zur Fehlerbehandlung:
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;
}
}
Abo-Management
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);
}
Konfiguration wechseln: Es kann immer nur eine aktive Sitzung gleichzeitig existieren. Um die Konfiguration zu wechseln, rufen Sie destroy() und dann initialize() auf:
import { destroy, initialize } from '@braze/javascript-sdk';
destroy();
await initialize({ /* new config */ });
Häufige Anwendungsfälle
Nutzeridentifikation und Attribut-Tracking
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']);
Ereignis-Logging und Analytics
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);
});
Handhabung von In-App Messages
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);
});
Fehlerbehandlung und Grenzfälle
Häufige Fehlerbedingungen
SDK nicht initialisiert:
- Die meisten Methoden geben
undefinedzurück (statt zu werfen), wenn das SDK nicht initialisiert ist initialize()gibtfalsezurück, wenn bereits initialisiert oder die Validierung fehlschlägtchangeUser()ist ein No-Op und das Promise wird aufgelöst, wenn das SDK nicht initialisiert ist- Prüfen Sie immer auf
undefined-Rückgabewerte, bevor Sie sie verwenden
Validierungsfehler:
- Ungültiger API-Schlüssel oder Base-URL:
initialize()gibtfalsezurück und protokolliert einen Fehler - Ungültige Ereignisnamen/Schlüssel: Maximal 255 Zeichen, darf nicht mit
$beginnen, nur alphanumerische Zeichen und Satzzeichen - Ungültige Attributwerte: Strings maximal 255 Zeichen, keine Zeilenumbrüche/Tabs/doppelte Anführungszeichen, darf nicht mit
$beginnen - Ungültige Währungscodes: Nicht unterstützte Codes führen zu einer Warnung, es wird keine Aktion ausgeführt
- Ungültige Kaufmenge: Muss zwischen 1 und 100 liegen, wird andernfalls ignoriert
Netzwerkfehler:
postRequest()des NetworkManagers sollte Fehler behandeln und Promises entsprechend ablehnen- Der Data-Flush-Controller wiederholt fehlgeschlagene Anfragen automatisch
- Verwenden Sie den Callback von
requestImmediateDataFlush(), um Flush-Fehler zu erkennen
Speicherfehler:
- StorageManager-Methoden sollten Fehler ordnungsgemäß behandeln
- Wenn die Speicherung fehlschlägt, funktioniert das SDK möglicherweise nicht korrekt
- Das
isId-Flag bestimmt die Persistenz: IDs bleiben über Sitzungen hinweg bestehen, Objekte sind sitzungsbezogen
Grenzfälle bei der Nutzeridentifikation:
- Nach der Identifikation kann nicht zu anonymen Nutzer:innen zurückgekehrt werden
- Ein Nutzerwechsel beendet die aktuelle Sitzung und startet eine neue
- Der Verlauf anonymer Nutzer:innen bleibt bei der erstmaligen Identifikation erhalten
- Der Verlauf wird zusammengeführt, wenn die:der Nutzer:in auf einem anderen Gerät existiert
Sitzungsmanagement:
- Sitzungen laufen nach 30 Minuten Inaktivität ab (konfigurierbar)
openSession()gibttruefür eine neue Sitzung zurück,falsebei WiederaufnahmeopenSession()muss nachchangeUser()odersetIdentifierToken()aufgerufen werden
Abo-Management:
- Abo-Callbacks werden synchron ausgelöst, wenn Ereignisse auftreten
- Entfernen Sie Abos, um Speicherlecks zu vermeiden
removeAllSubscriptions()löscht alle Abos auf einmal
Data-Flushing:
- Automatischer Flush alle 10 Sekunden (konfigurierbar, Minimum: 3 Sekunden)
- Flush kann stillschweigend fehlschlagen – verwenden Sie den Callback von
requestImmediateDataFlush() - Daten werden in die Warteschlange gestellt, wenn das Netzwerk nicht verfügbar ist, und übertragen, wenn die Verbindung wiederhergestellt wird
Wichtige Hinweise zur Implementierung
-
Die meisten Methoden sind asynchron: Asynchrone SDK-Methoden geben ein Promise zurück (verwenden Sie
awaitoder.then()). Einige Konfigurations- und Hilfsmethoden (z. B.destroy,toggleLogging,setLogger) sind synchron; Details finden Sie in den TypeScript-Definitionen oder der Kurzreferenztabelle. -
Methoden können
undefinedzurückgeben: Wenn das SDK nicht initialisiert ist, geben die meisten Methodenundefinedzurück, anstatt einen Fehler zu werfen. Prüfen Sie aufundefined, bevor Sie Rückgabewerte verwenden. -
Methoden können
nullzurückgeben: Einige Methoden gebennullzurück, um „nicht gefunden“ anzuzeigen (z. B. gibtgetUserId()nullzurück, wenn die:der Nutzer:in anonym ist). Dies unterscheidet sich vonundefined(SDK nicht initialisiert). - Speicherschlüssel verwenden das
isId-Flag: Der ParameterisIdin den StorageManager-Methoden unterscheidet zwischen:- ID-Speicherung: Persistente Bezeichner (Geräte-ID, Nutzer-ID), die über Sitzungen hinweg bestehen bleiben sollten
- Objekt-Speicherung: Sitzungsbezogene Daten, die gelöscht werden können
-
SDK-Metadaten-Tags: Das Array
sdkMetadataidentifiziert die Plattform bzw. den Wrapper, der das SDK verwendet (z. B.['npm']oder[BrazeSdkMetadata.NPM]). Gültige Tags werden durch das EnumBrazeSdkMetadatadefiniert (wienpm,cdn,manu,shp,gg,kep), und das SDK fügt automatisch'wjs'hinzu, um das JavaScript SDK zu kennzeichnen. -
Standard-NetworkManager: Wenn kein
networkManagerangegeben wird, verwendet das SDK eine Standardimplementierung, die globalefetch- undURL-APIs voraussetzt. Stellen Sie eine angepasste Implementierung bereit, falls diese nicht verfügbar sind. -
PushManager ist optional: Implementieren Sie
PushManagernur, wenn Sie Push-Benachrichtigungsfunktionen benötigen. Andernfalls kann er weggelassen werden. -
Destroy und Bereinigung: Rufen Sie
destroy()auf, wenn Sie das SDK herunterfahren müssen. Es kann immer nur eine aktive Sitzung gleichzeitig existieren; Sie müssendestroy()aufrufen, bevor Sieinitialize()erneut aufrufen. Dadurch werden Timer gestoppt, Daten übertragen und Ressourcen freigegeben. -
Data-Flushing: Daten werden automatisch alle 10 Sekunden übertragen (konfigurierbar). Verwenden Sie
requestImmediateDataFlush()für die sofortige Synchronisierung. -
Sitzungsmanagement: Rufen Sie
openSession()immer nachchangeUser()odersetIdentifierToken()auf, um das Erstellen doppelter anonymer Nutzer:innen zu vermeiden. -
Typsicherheit: Das SDK ist in TypeScript mit vollständigen Typdefinitionen geschrieben. Verwenden Sie TypeScript für die beste Entwicklungserfahrung und Typprüfung.
- Validierungsregeln: Ereignisnamen, Attributschlüssel und Eigenschaftsschlüssel unterliegen einer strengen Validierung (maximal 255 Zeichen, darf nicht mit
$beginnen, nur alphanumerische Zeichen und Satzzeichen). Ungültige Werte können ignoriert werden oder Fehler verursachen.
Debugging / Fehlerbehebung
Übergeben Sie die Option enableLogging: true an die Initialisierungsoptionen. Dies ist für die Entwicklung hilfreich, aber stellen Sie sicher, dass Sie diese Option entfernen oder einen alternativen Logger bereitstellen, bevor Sie Ihre Seite in die Produktionsumgebung überführen.
Kontakt
Bei Fragen wenden Sie sich bitte an [email protected].
Repository-Details und Beispielprojekte finden Sie unter https://github.com/braze-inc/braze-javascript-sdk.