Server GTM Promise API. How to get / set user state and pass it to Google Ads Remarketing
The goal
In this post I’ll show how to use Promise API to get user state, modify a few user properties and pass these properties to Google Ads Remarketing Tag.
How to do it
Send a login event from a Web GTM container to Server GTM;
On Server GTM get user state by API. Get loginCount and sessionsCount values from a user state. These two names are only for learning purposes - to show Promise.all in all it’s beauty;
Increase loginCount, sessionsCount by one;
Save new user state by API;
Generate a new event with changed loginCount, sessionsCount;
Fire Google Ads Remarketing Tag on this event and pass loginCount, sessionsCount to Google.
User state API
Let’s pretend that IT department provided us an API to deal with user property:
To get all user data: GEThttps://…${userId}/data/
To set user property PUThttps://…${userId}/data/${propertyName} with body {propertyName: propertyValue}
And API has simple authorization by token.
But if you don’t have special API, you can store user state in Google Firestore, please read how to use Firestore and GTM together.
Implementation
Step 1.
It’s easy because it was done all the time before Server GTM.
Step 2 - 5.
Let’s create a ServerSide GTM Template. Later we will add a Tag based on this template and will fire this tag on the login event. The template will implement steps from 2 - 5.
First I’ll show all the code and later we will go line by line:
// Get event params
constuserId=getEventData("user_id");
constallEventData=getAllEventData();
// Without user_id we can't get or modify user properties
if (!userId) {
logToConsole("Event doesn't have userId");
data.gtmOnFailure();
}
else{
// Do the logic
getData(init);
}
Here we take userId from the event, If userId is undefined then we can do nothing. Also don’t forget to call data.gtmOnFailure(); for each error case.
If userId exists, call the getData and pass a callback function to it.
Next lines 112 - 127 getData function declaration:
// Get user data and pass to callback
functiongetData(callback) {
sendHttpRequest(
getGetEndpoint(),
{ headers:getHeaders(), method:"GET", timeout:5000 },
"" )
.then((result) => {
constuserData=prepareBody(result.body);
callback(userData);
})
.catch((error) => {
logToConsole("getData error", error);
data.gtmOnFailure();
});
}
The function uses sendHttpRequest API then result is ready, clear userDate and pass it to callback.
If you don’t understand what’s going on here the best starting point is the Simo’s post about ASYNCHRONOUS VARIABLES IN SERVER-SIDE GOOGLE TAG MANAGER. Yes, you are right Simo is doing great work for the whole GTM / GA / GCP and so on community that’s why I decided to uppercase his article (and also I just copied the title as it is).
Next in lines 129 - 132 we define simple callback function.
129
130
131
132
// Callback to change user properties
functioninit(userData) {
increaseProperties(userData, ["loginCounter", "sessionCounter"]);
}
It calls increaseProperties methods and passes an array of properties we want to increase.
increaseProperties takes lines 55 - 110. Let’s break it into parts. The first part is
57
58
59
60
61
62
63
64
65
66
67
68
69
// Prepare request params for each property we want to change
letrequestsParams= [];
propertyNames.forEach((propertyName) => {
if (userData.hasOwnProperty(propertyName)) {
// Increase params also in user state
userData[propertyName] =makeNumber(userData[propertyName]) +1;
} else {
userData[propertyName] =1;
}
requestsParams.push(
getPutRequestParam(propertyName, userData[propertyName])
);
});
We increase all properties by one if it already exists but set it to one if it’s a new property. Also we prepare body and endpoint for each PUT request and add them to requestsParams array.
Here’s the beauty of the Promise API. No more inherent callbacks or other weird nasty things. Just clear Promise interface. I promise you will like it.
.then((results) => {
if (
results.filter((result) => result.statusCode===200).length===requestsParams.length ) {
// Add current user state to event
Object.keys(userData).forEach((key) => {
if (!allEventData.hasOwnProperty(key))
allEventData[key] =userData[key];
});
// Set new event name
allEventData.event_name="login_init";
// Run new event
runContainer(allEventData, () => returnResponse());
data.gtmOnSuccess();
} else {
logToConsole("Counters not set");
data.gtmOnFailure();
}
})
First we check all requests return status 200 (sometimes it can be other 2XX statuses for example 201).
Next add all user data to event data. Set new name and run runContainer API to generate an event with a new name.
That’s all! Now we’ve increased user properties and got the new event with all needed user data. The fun part is ending. Let’s go to the boring “profit part”.
Step 6.
Let’s add two Event Data Variables for the two user properties we’ve just set.
Add trigger for the new event name
And add Google Ads Remarketing Tag
Profit. Now your marketing team is happier than ever as they can build audiences in Google Ads based on new properties. And your Dev team is happy too, as you move out from the front end all these “useless pixels” which only slow down our cool site.
P.S. If you, like me, are a great fan of Testing API you can read my next post about the way we can test Promise API. See you there!