Event id
Event id should be a unique identifier of the event. By default it's a combination of:
- user_pseudo_id (aka client_id)
 - event_timestamp
 - event_name
 - engagement_time_msec (if the event has this parameter)
 
But sometimes even this combination could be not unique. The main problem is that GA4 could send a few events in one bacth and event_timestamp isn't the time of the event but the time of the batch.
One way to guarantee uniqueness is to add a custom timestamp event parameter (for example gtm_event_timestamp) in your GTM setup. After that you could set timestampEventParamName property to use a custom event parameter as a part of event_id, like this:
const ga4 = require("dataform-ga4-sessions");// Define your configconst config = {  dataset: "analytics_XXXXXX",  incrementalTableName: "events_XXXXXX",};// Create event objectlet purchase = new ga4.Event(eventConfig);purchase.setEventName("purchase");purchase.timestampEventParamName = "gtm_event_timestamp";Or you could set this property on EventFactory level to use it for all events:
const ga4 = require("dataform-ga4-sessions");// Define your configconst config = {  dataset: "analytics_XXXXXX",  incrementalTableName: "events_XXXXXX",};// Create event factory objectconst ef = new ga4.EventFactory(config);ef.timestampEventParamName = "gtm_event_timestamp";// Create all recommended form tracking eventslet pageView = ef.createPageView();note
But if you use a custom timestamp event parameter, it is expected that it should be int_value. If it's for example string or float the package couldn't get this value.
Change event_id defenition#
To generate event_id by default the package uses getSqlEventId helper function.
But you could change the logic of event_id generation and provide your own SQL code, like this:
let ef = new ga4.EventFactory(config);let pageView = ef.createPageView();pageView.getSqlUniqueId = () => {  return `FARM_FINGERPRINT(CONCAT(event_timestamp, event_name, user_pseudo_id, ifnull((select ep.value.int_value from unnest(event_params) as ep where ep.key = 'engagement_time_msec' ),0))) as event_id`;};Or you could create a unique event_id using your custom event_id set on GTM side. For example, if you added gtm_event_id event parameter, you could use it like this:
let ef = new ga4.EventFactory(config);let pageView = ef.createPageView();pageView.getSqlUniqueId = () => {  return `FARM_FINGERPRINT(CONCAT(event_timestamp, event_name, user_pseudo_id, ifnull((select ep.value.int_value from unnest(event_params) as ep where ep.key = 'gtm_event_id' ),0))) as event_id`;};In this example we use not only gtm_event_id but other values (event_timestamp,event_name,user_pseudo_id) because auto generated events like session_start could miss your custom parameter or have the same initial value.
note
If you want to change event_id don't forget to add event_id alias ("as event_id" at the end) to your custom SQL code.
Delete events without event_id#
By default, the package deletes all events without event_id. And also publish method returns only one row for each event_id using QUALIFY:
QUALIFY ROW_NUMBER() OVER (PARTITION BY event_id) = 1But you could change this behaviour by calling skipUniqueEventsStep() method on event object:
const ga4 = require("dataform-ga4-sessions");// Define your configconst config = {  dataset: "analytics_XXXXXX",  incrementalTableName: "events_XXXXXX",};// Create event objectlet purchase = new ga4.Event(eventConfig);purchase.setEventName("purchase");purchase.skipUniqueEventsStep();