LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Blog-Übersicht /Digital Marketing / Serverseitiges GTM: CRM-Daten aus Firestore und BigQuery...
Diesen Artikel in anderen Sprachen lesen:

Serverseitiges GTM: CRM-Daten aus Firestore und BigQuery ergänzen

Serverseitiges GTM: CRM-Daten aus Firestore und BigQuery ergänzen
Inhalt
  1. Weniger Browserdaten, klare Grenzen
  2. Firestore lesen, BigQuery vorberechnen
  3. Ein begrenzter Lookup-Kern
  4. Berechtigungen und Laufzeit
  5. Empfänger und Betrieb prüfen
  6. Fragen und Antworten
  7. Quellen

Serverseitige Datenanreicherung ergänzt ein Messereignis um ausgewählte Angaben aus einem Backend. Ein Beispiel ist ein zuvor berechnetes Kundenwertsegment. Dadurch müssen interne CRM-Felder nicht im Browser stehen. Die Verlagerung auf einen Server macht Daten jedoch weder anonym noch automatisch zur Übermittlung an Werbeplattformen geeignet.

Weniger Browserdaten, klare Grenzen

Eine Kundennummer oder gehashte Kennung bleibt bei möglicher Rückverknüpfung personenbezogen beziehungsweise pseudonymisiert. Hashing ist keine Verschlüsselung und kein allgemeiner Anonymitätsnachweis. Auch ein Session-Token kann besonders schutzbedürftig sein: Zugangstoken und Authentifizierungscookies gehören nicht in Analytics-Ereignisse.

Ein geeignetes Konzept trennt eine für Messzwecke vorgesehene Kennung von der Anmeldung. Der Server muss den erlaubten Bezug zum Kundenkonto verifizieren. Eine frei änderbare user_id aus einer öffentlichen Browseranfrage darf keinen beliebigen CRM-Datensatz auswählen. Eine syntaktisch korrekte Kennung allein genügt dafür nicht.

Stufe Aufgabe
Browser Nur benötigte Ereignisfelder und gegebenenfalls eine zulässige pseudonyme Kennung senden.
Geprüftes Backend Identität, Ereignisberechtigung und zulässigen Anreicherungszweck feststellen.
Anreicherungsdatenbank Wenige freigegebene Attribute mit Aktualisierungszeitpunkt bereitstellen.
Ziel-Tag Nur erlaubte Felder im passenden Zielschema und unter den erforderlichen Consent-Regeln übertragen.
Geprüfte Kontozuordnung, minimale Firestore-Daten, zielbezogene Regeln und ausdrückliche Fehlerzustände.
Geprüfte Kontozuordnung, minimale Firestore-Daten, zielbezogene Regeln und ausdrückliche Fehlerzustände.

Firestore lesen, BigQuery vorberechnen

Firestore eignet sich als Dokumentenspeicher für gezielte Schlüsselabfragen. Antwortzeiten hängen unter anderem von Region, Netzwerk und Last ab. Eine Sub-Millisekunden- oder verzögerungsfreie Antwort ist für den gesamten ssGTM-Aufruf nicht zugesichert. Geeignet sind Messungen von typischer und hoher Latenz unter realer Last.

BigQuery dient hier zur analytischen Berechnung, etwa eines Kundenwertsegments aus Bestellhistorien. Ein eigener, überwachter Exportjob schreibt die freigegebenen Ergebnisse nach Firestore. Dieser Export entsteht nicht automatisch durch die Verbindung der Produkte. Berechnungsstand, Exportfehler und Löschungen müssen berücksichtigt werden. Der Requestpfad liest anschließend vorberechnete Werte, statt für jedes Ereignis eine komplexe Warehouse-Abfrage zu starten.

Ein begrenzter Lookup-Kern

Das folgende Beispiel ist der Code einer benutzerdefinierten serverseitigen Variablen, kein vollständiges Template. Drei Template-Felder sind erforderlich: projectId als feste Projektkonfiguration, authorizedCustomerKey aus einer verifizierten serverseitigen Zuordnung und lookupAllowed als boolesche Entscheidung der geprüften Berechtigungs-/Consent-Logik. Direktes Kopieren dieser Werte aus Browserfeldern erfüllt die Voraussetzung nicht.

Die separate Sammlung enrichment enthält ausschließlich freigegebene Attribute. updated_at_ms ist eine vom Export geschriebene Unixzeit in Millisekunden, kein Firestore-Timestampobjekt. Die Segmentwerte und die Frist von 24 Stunden sind Beispielregeln und müssen zum tatsächlichen Datenmodell passen.

const Firestore = require('Firestore');
const getType = require('getType');
const queryPermission = require('queryPermission');
const getTimestampMillis = require('getTimestampMillis');

// Template inputs: trusted server-side mapping, never raw browser fields.
if (data.lookupAllowed !== true) return {status: 'not_allowed'};
const key = data.authorizedCustomerKey;
if (getType(key) !== 'string' || key.length < 16 || key.length > 128) {
  return {status: 'invalid_key'};
}
const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
for (let i = 0; i < key.length; i++) {
  if (alphabet.indexOf(key.charAt(i)) < 0) return {status: 'invalid_key'};
}
if (getType(data.projectId) !== 'string' || data.projectId.length === 0) {
  return {status: 'invalid_project'};
}
const path = 'enrichment/' + key;
if (!queryPermission('access_firestore', 'read', {projectId: data.projectId, path: path})) {
  return {status: 'permission_denied'};
}
const maxAgeMs = 86400000; // Example freshness policy: 24 hours.
return Firestore.read(path, {
  projectId: data.projectId
}).then((result) => {
  const record = result.data;
  if (getType(record) !== 'object' || record === null) {
    return {status: 'invalid_record'};
  }
  const timestamp = record.updated_at_ms;
  const now = getTimestampMillis();
  if (getType(timestamp) !== 'number' || !(timestamp > 0) || timestamp > now) {
    return {status: 'invalid_timestamp'};
  }
  if (now - timestamp > maxAgeMs) return {status: 'stale'};
  const segment = record.ltv_segment;
  if (segment !== 'low' && segment !== 'medium' && segment !== 'high') {
    return {status: 'invalid_segment'};
  }
  return {status: 'ok', attributes: {ltv_segment: segment}};
}, (error) => {
  return {status: error && error.reason === 'not_found' ? 'not_found' : 'lookup_error'};
});

Firestore.read liefert ein Objekt mit id und data; die Felder liegen unter result.data. Ein fehlendes Dokument führt zur Ablehnung mit reason: 'not_found'. Der Code unterscheidet daher fehlende, veraltete, ungültige und technisch nicht gelesene Daten. Unbekannte Werte werden weder als Marge null noch als Standardkunde ausgegeben.

Das Ziel-Tag darf nur bei status === 'ok' ausdrücklich zugeordnete Attribute übernehmen. Das interne Statusobjekt ist kein Versandformat. Auch ein erfolgreicher Lookup ersetzt keine empfängerbezogene Freigabe. Bei Fehlern ist je Ereignis festzulegen, ob ein bereits erlaubtes Basisereignis ohne Zusatzfelder gesendet oder die Weiterleitung unterdrückt wird.

Berechtigungen und Laufzeit

Die eingebaute ssGTM-Firestore-API unterstützt laut Dokumentation Native Mode und ausschließlich die Standarddatenbank (default). Für eine isolierte Datenbank kann ein separates Projekt mit eigener Standarddatenbank verwendet werden. Das Template benötigt nur access_firestore für Lesen, das konkrete Projekt und den vorgesehenen Pfad. Diese Template-Grenze ergänzt IAM, ersetzt IAM aber nicht.

Der Laufzeitdienst erhält eine eigene Identität mit minimalen Leserechten. Der Exportjob verwendet eine getrennte Schreibidentität. Serverzugriffe über solche Identitäten werden durch IAM kontrolliert; Browser-Security-Rules schützen diesen Pfad nicht. Eine Sammlung mit wenigen Feldern und eine getrennte Projektgrenze reduzieren den Datenumfang, auf den ein kompromittierter Dienst zugreifen könnte.

Ein Lookup verlängert die davon abhängige Tagverarbeitung. Ob die Browserantwort ebenfalls wartet, hängt vom Client und dessen Antwortsteuerung ab. Die dokumentierten Optionen von Firestore.read enthalten keinen eigenen timeout-Wert; unbekannte Optionen werden ignoriert. Sandboxed-JavaScript-Promises haben eine dokumentierte Fünf-Sekunden-Grenze. Das ist kein geeigneter Ersatz für ein bewusst gewähltes kurzes Antwortbudget. Strengere Fristen können einen eigenen Lookup-Dienst oder einen asynchronen Ablauf erfordern.

Empfänger und Betrieb prüfen

GA4 und Meta CAPI haben unterschiedliche Datenmodelle und Vorgaben. Ein Hash erlaubt keine beliebige Übertragung personenbezogener Angaben als Eventparameter. Google bietet dafür besondere, ausdrücklich einzurichtende Funktionen für User-provided Data; diese Funktionen sind vom normalen User-ID-Feld und frei benannten Parametern zu unterscheiden. Margen und Segmente sollten nur an tatsächlich erforderliche Empfänger gelangen.

Zur Betriebsprüfung gehören Trefferrate, fehlende Dokumente, veraltete Exporte, technische Fehler und Latenzverteilung. Aggregierte Zähler vermeiden unnötige CRM-Inhalte in Logs. Kosten umfassen nicht nur Reads, sondern unter anderem Export-Schreibvorgänge, Speicherung und gegebenenfalls Netzwerkverkehr. Der API-Cache gilt standardmäßig für die jeweilige Request-/Container-Ausführung, nicht als globaler Kunden-Cache.

Fragen und Antworten

Warum kein Ersatzwert null?

Ein unbekannter Wert und eine echte Null haben unterschiedliche Bedeutung. Ein Ersatzwert kann Auswertungen verfälschen. Fehlerstatus und fehlendes Zusatzfeld erhalten diese Unterscheidung.

Garantiert der Server Datenleckfreiheit?

Nein. Antworten an den Browser, Debugausgaben, Logs, Exporte und ausgehende Requests bleiben mögliche Datenwege. Die konkrete Konfiguration und die tatsächlich übertragenen Felder müssen geprüft werden.

Was ist mit dem Beispiel geprüft?

29 isolierte Prüfungen mit simulierten APIs decken Eingabegrenzen, Lesesperren, fehlende und veraltete Daten sowie die erlaubte Feldauswahl ab. Ein Import in die GTM-Sandbox, reale IAM-Rechte, Lastverhalten und Hersteller-Payloads sind damit nicht nachgewiesen.

Quellen

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Kontakt

Eine Frage oder eine Idee für ein Webprojekt? Schreib mir kurz, worum es geht. Das Formular nutzt reCAPTCHA zum Schutz vor Spam.

Kommentar schreiben

Erfahrungen aus der Praxis und Rückfragen zur Umsetzung sind hier willkommen.

Die E-Mail-Adresse wird nicht veröffentlicht. Pflichtfelder sind mit einem Stern versehen.