{"id":72371,"date":"2025-02-06T22:47:13","date_gmt":"2025-02-06T22:47:13","guid":{"rendered":"https:\/\/peraltafinancing.com\/analytics\/write-to-google-bigquery-from-a-gtm-server-container\/"},"modified":"2025-02-06T22:47:13","modified_gmt":"2025-02-06T22:47:13","slug":"write-to-google-bigquery-from-a-gtm-server-container","status":"publish","type":"post","link":"https:\/\/fivemor.com\/?p=72371","title":{"rendered":"Write To Google BigQuery From A GTM Server Container"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>Ever since it was released that <a href=\"https:\/\/www.simoahava.com\/analytics\/server-side-tagging-google-tag-manager\/\">server-side tagging in Google Tag Manager<\/a> would run on the <strong>Google Cloud Platform<\/strong> stack, my imagination has been running wild.<\/p>\n<p>By running on GCP, the potential for integrations with <em>other<\/em> GCP components is limitless. The output to <a href=\"https:\/\/www.simoahava.com\/analytics\/tips-for-logging-server-side-tagging\/\">Cloud Logging<\/a> already introduces interesting pipeline opportunities, but now it gets even better.<\/p>\n<p><strong>It\u2019s finally possible to write directly to Google BigQuery from a <a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api\">Client or tag template<\/a>!<\/strong><\/p>\n<div style=\"aspect-ratio: 1202 \/ 452;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bigquery-in-template.jpg\" title=\"BigQuery in a custom template\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"452\" width=\"1202\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bigquery-in-template.jpg#ZgotmplZ\" alt=\"BigQuery in a custom template\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>This means that instead of treating server-side tagging as a \u201csimple\u201d <strong>proxy<\/strong>, whose purpose is mainly to replace data streams that would otherwise connect a client directly with an endpoint, it\u2019s now possible to use a Server container as a data collection machine itself.<\/p>\n<p>In this article, I\u2019ll show you <strong>how to use the BigQuery API<\/strong> with a use case <a href=\"https:\/\/www.simoahava.com\/analytics\/google-tag-manager-monitor\/\">you might already be familiar with<\/a>. We\u2019re going to build a <em>monitoring system<\/em> that writes tag execution data into a Google BigQuery table.<\/p>\n<p>This won\u2019t be the last I\u2019ll write about this integration \u2013\u00a0there are a <strong>lot<\/strong> of possibilities available now that a way to natively write data to a data warehouse from the Server container has become available.<\/p>\n<p>We\u2019ll start with a <strong>walkthrough<\/strong> before diving into the <a href=\"#the-bigquery-api\"><strong>API specs<\/strong><\/a>.<\/p>\n<p>                <span class=\"simmer\"><br \/>\n  <span class=\"close\">X<\/span><\/p>\n<p>\n    <span class=\"fa fa-md fa-bell\"\/><br \/>\n    <strong>The Simmer Newsletter<\/strong>\n  <\/p>\n<p>\n    Subscribe to the <a href=\"https:\/\/www.simoahava.com\/newsletter\/\">Simmer newsletter<\/a> to get the latest news and content from Simo Ahava into your email inbox!\n  <\/p>\n<p>  <\/span><\/p>\n<h2 id=\"official-documentation\">Official documentation<\/h2>\n<p>You can find the official documentation for the BigQuery template API by following this link::<\/p>\n<p><a href=\"https:\/\/developers.google.com\/tag-manager\/serverside\/api#bigquery\">Server-side Tagging APIS: BigQuery<\/a><\/p>\n<h2 id=\"full-example-build-a-monitoring-system\">Full example: build a monitoring system<\/h2>\n<p>To get started, we\u2019ll build a <a href=\"https:\/\/www.simoahava.com\/analytics\/google-tag-manager-monitor\/\">Google Tag Manager monitor<\/a> for your server-side tags.<\/p>\n<div style=\"aspect-ratio: 1640 \/ 890;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bigquery-monitor.jpg\" title=\"BigQuery monitor\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"890\" width=\"1640\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bigquery-monitor.jpg#ZgotmplZ\" alt=\"BigQuery monitor\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The <strong>schema<\/strong> is very simple. It\u2019s designed to collect information about the Client that caused the tag(s) to fire, whether they fired successfully (or failed), and how long the execution time of the tag was.<\/p>\n<p>You can use this to proactively monitor for issues in your Server container setup.<\/p>\n<h3 id=\"create-the-table\">Create the table<\/h3>\n<p>Before we get started, we need to create a <strong>table<\/strong> in BigQuery. I recommend using the same project as the one where your Server container is running. This way you don\u2019t have to worry about setting up authentication, as the Server container will already have full access to all cloud components running in the same project.<\/p>\n<p>First, <a href=\"https:\/\/console.cloud.google.com\/bigquery\">Go to BigQuery<\/a>.<\/p>\n<p>Then, find your project ID in the navigation, and select the project.<\/p>\n<p>Finally, click <strong>CREATE DATASET<\/strong> to create a new dataset.<\/p>\n<div style=\"aspect-ratio: 2002 \/ 900;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/create-dataset.jpg\" title=\"Create dataset\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"900\" width=\"2002\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/create-dataset.jpg#ZgotmplZ\" alt=\"Create dataset\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Give dataset an ID, such as <code>gtm_monitoring<\/code>, and set the data location, if you wish. Once ready, click the <strong>Create dataset<\/strong> button.<\/p>\n<p>Once the dataset is created, make sure it\u2019s selected in the navigation, and then click <strong>CREATE TABLE<\/strong>.<\/p>\n<div style=\"aspect-ratio: 2270 \/ 776;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/create-table.jpg\" title=\"Create table\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"776\" width=\"2270\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/create-table.jpg#ZgotmplZ\" alt=\"Create table\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The three settings you\u2019ll want to edit are <strong>Table name<\/strong>, <strong>Schema<\/strong>, and <strong>Partitioning<\/strong>.<\/p>\n<p>Set the <strong>Table name<\/strong> to whatever you want to name the table as, e.g. <code>event_data<\/code>.<\/p>\n<p>Next, click <strong>+Add field<\/strong> to start creating the <strong>schema<\/strong>.<\/p>\n<p>The schema is essentially a blueprint for what type of data the table expects to be inserted into it. You need to define the <strong>fields<\/strong> (or columns) of the table, whether they are <strong>required<\/strong> or something else, and what the expected <strong>data type<\/strong> is.<\/p>\n<p>See <a href=\"https:\/\/cloud.google.com\/bigquery\/docs\/schemas\">the BigQuery documentation<\/a> for more details on what options you have at your disposal.<\/p>\n<p>This is the schema I\u2019ve chosen for the monitoring data:<\/p>\n<table>\n<thead>\n<tr>\n<th>Field name<\/th>\n<th>Type<\/th>\n<th>Mode<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>event_name<\/code><\/td>\n<td>STRING<\/td>\n<td>REQUIRED<\/td>\n<\/tr>\n<tr>\n<td><code>event_timestamp<\/code><\/td>\n<td>INTEGER<\/td>\n<td>REQUIRED<\/td>\n<\/tr>\n<tr>\n<td><code>client_name<\/code><\/td>\n<td>STRING<\/td>\n<td>REQUIRED<\/td>\n<\/tr>\n<tr>\n<td><code>tag<\/code><\/td>\n<td>RECORD<\/td>\n<td>REPEATABLE<\/td>\n<\/tr>\n<tr>\n<td><code>tag.id<\/code><\/td>\n<td>STRING<\/td>\n<td>NULLABLE<\/td>\n<\/tr>\n<tr>\n<td><code>tag.name<\/code><\/td>\n<td>STRING<\/td>\n<td>NULLABLE<\/td>\n<\/tr>\n<tr>\n<td><code>tag.status<\/code><\/td>\n<td>STRING<\/td>\n<td>NULLABLE<\/td>\n<\/tr>\n<tr>\n<td><code>tag.execution_time<\/code><\/td>\n<td>INTEGER<\/td>\n<td>NULLABLE<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>For <strong>Partitioning<\/strong>, I\u2019ve chosen to partition by <strong>day<\/strong>, which means that a table suffix is added to each row with the date of when the data was written to the table. This makes analysis much easier because you can scope your queries by day very easily.<\/p>\n<p>This is what the final configuration for the table looks like:<\/p>\n<div style=\"aspect-ratio: 1370 \/ 1794;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/table-settings.jpg\" title=\"Table settings\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1794\" width=\"1370\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/table-settings.jpg#ZgotmplZ\" alt=\"Table settings\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<h3 id=\"the-tag-template\">The tag template<\/h3>\n<p>You can find the template soon in the Community Template Gallery. For now, you can also download the <code>template.tpl<\/code> file by saving <a href=\"https:\/\/raw.githubusercontent.com\/gtm-templates-simo-ahava\/server-container-monitor\/main\/template.tpl\">this page<\/a> with that filename.<\/p>\n<p>To load the template in the container, go to the Server container, select <strong>Templates<\/strong>, and then <strong>create a new tag template<\/strong>.<\/p>\n<div style=\"aspect-ratio: 2484 \/ 1002;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/new-tag-template.jpg\" title=\"New tag template\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1002\" width=\"2484\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/new-tag-template.jpg#ZgotmplZ\" alt=\"New tag template\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Next, in the overflow menu, choose <strong>Import<\/strong>.<\/p>\n<div style=\"aspect-ratio: 1180 \/ 574;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/import-template.jpg\" title=\"Import template\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"574\" width=\"1180\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/import-template.jpg#ZgotmplZ\" alt=\"Import template\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Locate the <code>template.tpl<\/code> file you downloaded and import that using the system dialog.<\/p>\n<p>Click <strong>Save<\/strong> to save the template.<\/p>\n<p>To understand how the template works, read on\u2026<\/p>\n<h3 id=\"create-the-monitoring-tag\">Create the monitoring tag<\/h3>\n<p>Go to <strong>Tags<\/strong> in the Server container, and click <strong>NEW<\/strong> to create a new tag.<\/p>\n<div style=\"aspect-ratio: 2424 \/ 482;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/new-tag.jpg\" title=\"New tag\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"482\" width=\"2424\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/new-tag.jpg#ZgotmplZ\" alt=\"New tag\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Now you need to <strong>configure<\/strong> the tag. There are three fields you need to set:<\/p>\n<ul>\n<li><strong>Project ID<\/strong> \u2013 set to the GCP project ID of the project where the BigQuery table is. If it\u2019s the same project as the one running your Server container, <strong>you can leave this field blank<\/strong>.<\/li>\n<li><strong>Dataset ID<\/strong> \u2013 set to the Dataset ID of the BigQuery table.<\/li>\n<li><strong>Table ID<\/strong> \u2013 set to the Table ID of the BigQuery table.<\/li>\n<\/ul>\n<p>If you\u2019ve followed the examples of this tutorial, Dataset ID would be <code>gtm_monitoring<\/code> and Table ID would be <code>event_data<\/code>.<\/p>\n<p>Next, expand <strong>Advanced Settings<\/strong> and <strong>Additional Tag Metadata<\/strong>, and add a new metadata row with:<\/p>\n<p>This is what the tag should look like:<\/p>\n<div style=\"aspect-ratio: 1776 \/ 1346;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/server-container-tag-ready.jpg\" title=\"Server container tag ready\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1346\" width=\"1776\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/server-container-tag-ready.jpg#ZgotmplZ\" alt=\"Server container tag ready\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The <strong>Project ID<\/strong>, <strong>Dataset ID<\/strong>, and <strong>Table ID<\/strong> are used by the tag to make sure the data is written into the correct table.<\/p>\n<p>The <code>exclude<\/code> key in the <strong>Additional Tag Metadata<\/strong> can be used to exclude tags from being monitored. In this case, I find that monitoring the monitoring tag itself just adds to noise and confusion, which is why I\u2019ve chosen to exclude it.<\/p>\n<h3 id=\"add-the-trigger\">Add the trigger<\/h3>\n<p>Once you\u2019ve made the modifications to the monitoring tag, you need to <strong>add a trigger<\/strong> to it. This is quite simple \u2013\u00a0you need a trigger that fires for <strong>every single<\/strong> event because every single event has the capability of firing tags.<\/p>\n<p>So go to <strong>Triggers<\/strong> and click <strong>NEW<\/strong> to create a new trigger.<\/p>\n<p>This is what the <strong>All events<\/strong> trigger should look like:<\/p>\n<div style=\"aspect-ratio: 1026 \/ 504;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/all-events-trigger.jpg\" title=\"All events trigger\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"504\" width=\"1026\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/all-events-trigger.jpg#ZgotmplZ\" alt=\"All events trigger\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Add this trigger to your monitoring tag, and then save the tag.<\/p>\n<h3 id=\"edit-all-tags-metadata\">Edit all tags\u2019 metadata<\/h3>\n<p>The next step is to edit <strong>all the tags<\/strong> in the container. For each tag, you need to expand <strong>Additional Tag Metadata<\/strong> again.<\/p>\n<p>Here, you need to check <strong>Include tag name<\/strong>, and in the <strong>Key for tag name<\/strong> field you need to type <code>name<\/code>. Like so:<\/p>\n<div style=\"aspect-ratio: 886 \/ 386;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/key-for-tag-name.jpg\" title=\"Key for tag name\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"386\" width=\"886\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/key-for-tag-name.jpg#ZgotmplZ\" alt=\"Key for tag name\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>All the fields in <strong>Additional Tag Metadata<\/strong> are passed to the event callback which is set in the monitoring tag template itself. This callback is executed once <strong>all<\/strong> tags for that event have completed, and the metadata object gives additional details about the tags themselves.<\/p>\n<p>If you wish, you could add even more key-value pairs to the tag metadata \u2013 you just need to modify the monitoring template to add these to the BigQuery API call. And, of course, you need a BigQuery table schema that accepts the new values.<\/p>\n<h3 id=\"check-the-results-in-bigquery\">Check the results in BigQuery<\/h3>\n<p>At this point you should have the following:<\/p>\n<ul>\n<li>You\u2019ve <strong>imported the template<\/strong> from my repository (or the gallery).<\/li>\n<li>You\u2019ve <strong>created a new monitoring tag<\/strong> with the template.<\/li>\n<li>You\u2019ve <strong>configured<\/strong> the monitoring tag with your BigQuery table details.<\/li>\n<li>You\u2019ve <strong>added<\/strong> the <em>All events<\/em> trigger to the monitoring tag.<\/li>\n<li>You\u2019ve <strong>edited<\/strong> all the tags in the container, and you\u2019ve added the tag name as additional metadata to them.<\/li>\n<\/ul>\n<p>You can now either Preview the container (recommended), or you can just publish it. If you take the Preview route, you should see the <code>BigQuery success!<\/code> message in the <strong>Console tab<\/strong> of Preview mode when any event is selected:<\/p>\n<div style=\"aspect-ratio: 1814 \/ 498;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/console-tab.jpg\" title=\"Console tab\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"498\" width=\"1814\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/console-tab.jpg#ZgotmplZ\" alt=\"Console tab\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>If you see anything else, the error messages should tell you what the problem is (see <a href=\"#the-onfailure-callback\">this chapter<\/a> for more details).<\/p>\n<p>You can check <a href=\"https:\/\/console.cloud.google.com\/bigquery\">the data in BigQuery<\/a> as well. The easiest way to do this is to browse to the table itself, and then click Preview.<\/p>\n<div style=\"aspect-ratio: 1322 \/ 762;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/preview-bigquery.jpg\" title=\"Preview BigQuery\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"762\" width=\"1322\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/preview-bigquery.jpg#ZgotmplZ\" alt=\"Preview BigQuery\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>If you see rows here, it means it\u2019s working!<\/p>\n<p>You\u2019ve now built a simple monitoring system. While this is mostly a proof-of-concept, you can actually utilize this setup to see if tags are consistently signaling failure. Similarly, it should alert you if there are events where no tags fire (even though they should), or if there are unreasonably long execution times with your tags.<\/p>\n<p>And that\u2019s it for this simple walkthrough! Now it\u2019s time to <strong>dive into<\/strong> the API design itself.<\/p>\n<h2 id=\"the-bigquery-api\">The BigQuery API<\/h2>\n<p>To use the API, you need either a <strong>Client<\/strong> or a <strong>tag<\/strong> template. If you\u2019re unfamiliar with templates, check out these resources:<\/p>\n<blockquote>\n<p>Note, only a <strong>write<\/strong> API has been released thus far. We\u2019re anxiously waiting for a <strong>read<\/strong> API, so that data could be pulled in from BigQuery, too.<\/p>\n<\/blockquote>\n<p>The API itself is quite simple, but right now it has a caveat: <strong>the table you want to write to must exist already<\/strong>. Unlike some other BigQuery API clients, the custom template API does not automatically create a table for you if one doesn\u2019t already exist.<\/p>\n<p>This is good for control but bad for flexibility. It\u2019s possible this will change in the future. But until it does, you need to have a BQ table ready with a <strong>schema<\/strong> in place as well.<\/p>\n<p>The API is called like this:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#aaa;font-style:italic\">\/\/ Load the BQ API\n<\/span><span style=\"color:#aaa;font-style:italic\"\/><span style=\"color:#00a\">const<\/span> BigQuery = require(<span style=\"color:#a50\">'BigQuery'<\/span>);\n\nBigQuery.insert(connectionInfo, rows, options, onSuccess, onFailure);\n<\/code><\/pre>\n<\/div>\n<h3 id=\"the-connectioninfo-object\">The connectionInfo object<\/h3>\n<p>The first parameter is a <strong>connectionInfo<\/strong> object, and it looks like this:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\">{\n  projectId: <span style=\"color:#a50\">'gtm-abc123-z1def'<\/span>,\n  datasetId: <span style=\"color:#a50\">'gtm_monitoring'<\/span>,\n  tableId: <span style=\"color:#a50\">'event_data'<\/span>\n}\n<\/code><\/pre>\n<\/div>\n<p>Here are the keys you need to use in the object:<\/p>\n<table>\n<thead>\n<tr>\n<th>Key<\/th>\n<th>Sample value<\/th>\n<th>Required<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>projectId<\/code><\/td>\n<td><code>gtm-abc123-z1def<\/code><\/td>\n<td>No*<\/td>\n<td>Set to the Google Cloud project ID where the BigQuery dataset and table are. You can also omit this from the API call (see below for more information).<\/td>\n<\/tr>\n<tr>\n<td><code>datasetId<\/code><\/td>\n<td><code>gtm_monitoring<\/code><\/td>\n<td>Yes<\/td>\n<td>Set to the dataset ID of the BigQuery dataset which hosts the table.<\/td>\n<\/tr>\n<tr>\n<td><code>tableId<\/code><\/td>\n<td><code>event_data<\/code><\/td>\n<td>Yes<\/td>\n<td>Set to the table ID of the BigQuery table.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><code>projectId<\/code> is optional, but only in a specific circumstance. If the API call doesn\u2019t have <code>projectId<\/code> included, then you <strong>must<\/strong> set the <strong>Permissions<\/strong> for BigQuery so that Project ID is <em>either<\/em> <code>*<\/code> or <code>GOOGLE_CLOUD_PROJECT<\/code>.<\/p>\n<h3 id=\"permissions\">Permissions<\/h3>\n<p>When you set the Project ID permission to <code>GOOGLE_CLOUD_PROJECT<\/code>, the project ID is derived from an environment variable named <strong>GOOGLE_CLOUD_PROJECT<\/strong>. The good news is that if you\u2019re running this container on Google Cloud (as is the default), then the environment variable is automatically populated with the current project ID.<\/p>\n<div style=\"aspect-ratio: 1776 \/ 394;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bigquery-permissions-project-id.jpg\" title=\"BigQuery permissions\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"394\" width=\"1776\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bigquery-permissions-project-id.jpg#ZgotmplZ\" alt=\"BigQuery permissions\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>If you want to send the data to a table that\u2019s outside the current project, then it\u2019s easiest to just add the project ID to the permission list or to just use the wildcard (though the former is recommended over the latter).<\/p>\n<p>You can wildcard dataset ID and table ID, too, but you should only do this if you\u2019re designing the template for sharing via the Community Template Gallery.<\/p>\n<h3 id=\"the-rows-array\">The <code>rows<\/code> array<\/h3>\n<p>The second parameter in the <code>BigQuery.insert<\/code> call is the <code>rows<\/code> array.<\/p>\n<p>And this is where it gets tricky.<\/p>\n<p>The <code>rows<\/code> array <strong>must<\/strong> comply with the schema of the table if you want the data to be written correctly. You should try to use the types and formats required by the BigQuery table schema when building the objects in the <code>rows<\/code> array.<\/p>\n<p>For example, consider a schema like this:<\/p>\n<div style=\"aspect-ratio: 1176 \/ 824;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bq-schema.jpg\" title=\"BigQuery Schema\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"824\" width=\"1176\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bq-schema.jpg#ZgotmplZ\" alt=\"BigQuery Schema\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>To create a valid row for this schema, the object would look like this:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\">[{\n  event_name: <span style=\"color:#a50\">'page_view'<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/ String, required\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  event_timestamp: <span style=\"color:#a50\">'1614628341914'<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/ Integer, required\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  client_name: <span style=\"color:#a50\">'GA4'<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/String, required\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  tag: [{ <span style=\"color:#aaa;font-style:italic\">\/\/ Record, repeated\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    id: <span style=\"color:#a50\">'1'<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/ String, nullable\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    name: <span style=\"color:#00a\">undefined<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/ String, nullable\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    status: <span style=\"color:#a50\">'success'<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/ String, nullable\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    execution_time: <span style=\"color:#a50\">'3'<\/span> <span style=\"color:#aaa;font-style:italic\">\/\/ Integer, nullable\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  }, {\n    id: <span style=\"color:#a50\">'3'<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/ String, nullable\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    name: <span style=\"color:#a50\">'GA4'<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/ String, nullable\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    status: <span style=\"color:#a50\">'success'<\/span>, <span style=\"color:#aaa;font-style:italic\">\/\/ String, nullable\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    execution_time: <span style=\"color:#a50\">'10'<\/span> <span style=\"color:#aaa;font-style:italic\">\/\/ Integer, nullable\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  }]\n}]\n<\/code><\/pre>\n<\/div>\n<p>As you can see, each property set in a row object corresponds with a key in the BigQuery schema, and the values need to match the expected types.<\/p>\n<blockquote>\n<p>Note: \u201cInteger\u201d refers to the data format, not the JavaScript type. So it can be sent as a Number type or as a String type as in the example above.<\/p>\n<\/blockquote>\n<p>The most difficult things to get right, typically, are <strong>repeated records<\/strong>. Perhaps it\u2019s easier to think of this in JavaScript terms. A <strong>repeated record<\/strong> is essentially an <strong>array of objects<\/strong>. Or a <strong>list of dictionaries<\/strong> if Python vernacular suits you better.<\/p>\n<p>In any case, the object dispatched to the table <strong>must<\/strong> conform to the schema, otherwise you risk running into errors or getting patchy data.<\/p>\n<h3 id=\"the-options-object\">The <code>options<\/code> object<\/h3>\n<p>You can control what the API does when encountering invalid calls.<\/p>\n<p>The <code>options<\/code> object is the third parameter to the <code>BigQuery.insert<\/code> method, and it has the following keys:<\/p>\n<table>\n<thead>\n<tr>\n<th>Key<\/th>\n<th>Possible values<\/th>\n<th>Default<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>ignoreUnknownValues<\/code><\/td>\n<td><code>true<\/code> \/ <code>false<\/code><\/td>\n<td><code>false<\/code><\/td>\n<td>If set to <code>true<\/code>, then rows that have values that do <strong>not<\/strong> conform to the schema are accepted, but the unknown values are ignored in the write.<\/td>\n<\/tr>\n<tr>\n<td><code>skipInvalidRows<\/code><\/td>\n<td><code>true<\/code> \/ <code>false<\/code><\/td>\n<td><code>false<\/code><\/td>\n<td>If set to <code>true<\/code> then all <strong>valid<\/strong> rows of the request are inserted into the table, even if there are also invalid rows in the request.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>What you choose for these options depends on the type of data you want to collect. If you set <code>ignoreUnknownValues<\/code> to <code>true<\/code>, you might end up with a patchy table because expected values might end up missing from the table.<\/p>\n<p>If you set <code>skipInvalidRows<\/code> to <code>true<\/code>, it might be difficult to debug problems, as requests are allowed to complete even if they had invalid rows.<\/p>\n<h3 id=\"the-onsuccess-callback\">The <code>onSuccess<\/code> callback<\/h3>\n<p>You can <strong>optionally<\/strong> pass a method as an <code>onSuccess<\/code> callback. This method is invoked without any arguments, and you can use it to run some code after the rows were successfully inserted.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\">...\n<span style=\"color:#00a\">const<\/span> onBigQuerySuccess = () =&gt; {\n  log(<span style=\"color:#a50\">'BigQuery rows inserted successfully!'<\/span>);\n  data.gtmOnSuccess();\n};\n\nBigQuery.insert(connectionInfo, rows, {}, onBigQuerySuccess, data.gtmOnFailure);\n<\/code><\/pre>\n<\/div>\n<h3 id=\"the-onfailure-callback\">The <code>onFailure<\/code> callback<\/h3>\n<p>You can <strong>optionally<\/strong> pass a method as an <code>onFailure<\/code> callback. This method is invoked when an error occurs, and an array of all the errors that happened is passed automatically to this method.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\">...\n<span style=\"color:#00a\">const<\/span> onBQFailure = (errors) =&gt; {\n  log(<span style=\"color:#a50\">'BigQuery failure!'<\/span>);\n  log(errors);\n  data.gtmOnFailure();\n};\n<\/code><\/pre>\n<\/div>\n<blockquote>\n<p>Note that you don\u2019t generally have to log these explicitly, as the BigQuery errors will show up in the Console tab of Preview mode and in your <a href=\"https:\/\/www.simoahava.com\/analytics\/tips-for-logging-server-side-tagging\/\"><code>stdout<\/code> logs<\/a> regardless.<\/p>\n<\/blockquote>\n<p>If the error happened because insertion didn\u2019t happen at all, for example when trying to insert a row into a non-existent table, the error is simple:<\/p>\n<p>If, however, there is an error in the insertion itself, the array of errors is more complex. For every error encountered, an object is added to the array. This object has two keys:<\/p>\n<p><code>errors<\/code> is an array with a single object which has the <code>reason<\/code> key. This key is set to the <strong>reason why the error happened<\/strong> (e.g. <code>\"invalid\"<\/code>).<\/p>\n<p><code>row<\/code> is an object which describes the row itself where the error happened.<\/p>\n<div style=\"aspect-ratio: 2572 \/ 292;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/three-errors.jpg\" title=\"Three errors\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"292\" width=\"2572\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/three-errors.jpg#ZgotmplZ\" alt=\"Three errors\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In the image above, <strong>three<\/strong> errors were encountered. The first error was due to an invalid value being sent with the call. Due to <code>skipInvalidRows<\/code> being set to <code>false<\/code> (the default value), this results in the execution of the insert stopping, and all the remaining rows throw an error with the reason <code>stopped<\/code>.<\/p>\n<p>Note that it\u2019s completely optional to use the <code>onFailure<\/code> callback, but it might be a very good idea if you want to get to the bottom of missing or faulty data in your table!<\/p>\n<h2 id=\"authentication\">Authentication<\/h2>\n<p>As long as your Server container is running in the same GCP project as the BigQuery table, <strong>you\u2019re all set<\/strong>. You don\u2019t need to do anything special in terms of authentication, as the default service account of App Engine has full access to any BigQuery tables added to the project.<\/p>\n<p>If you want to write to a BigQuery table in <em>another<\/em> GCP project, you need to do the following:<\/p>\n<ol>\n<li>Locate the <em>App Engine default service account<\/em> (under <a href=\"https:\/\/console.cloud.google.com\/apis\/credentials\"><strong>APIs &amp; Services \/ Credentials<\/strong><\/a>) of the Server container project.<\/li>\n<li>Copy the email address of this service account to the clipboard.<\/li>\n<li>In the <a href=\"https:\/\/console.cloud.google.com\/iam-admin\/iam\"><strong>IAM<\/strong><\/a> of the GCP project with the BigQuery table, add a new member.<\/li>\n<li>Set the email address of the member to the email address in the clipboard.<\/li>\n<li>Set the role of this new member to <strong>BigQuery Data Editor<\/strong>.<\/li>\n<\/ol>\n<div style=\"aspect-ratio: 1944 \/ 350;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/service-account.jpg\" title=\"Service accounts in the Server container project.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"350\" width=\"1944\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/service-account.jpg#ZgotmplZ\" alt=\"Service accounts in the Server container project.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">Service accounts in the Server container project.<\/span><\/p>\n<\/div>\n<div style=\"aspect-ratio: 1272 \/ 984;\" class=\"figure \">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bigquery-data-editor.jpg\" title=\"New member creation in the project with the BigQuery table.\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"984\" width=\"1272\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2021\/03\/bigquery-data-editor.jpg#ZgotmplZ\" alt=\"New member creation in the project with the BigQuery table.\"\/><\/p>\n<p>    <\/a><\/p>\n<p>    <span class=\"caption\">New member creation in the project with the BigQuery table.<\/span><\/p>\n<\/div>\n<p>In other words, you are allowing the Server container GCP project to edit the BigQuery datasets in the target project.<\/p>\n<blockquote>\n<p>You can, of course, create a dedicated service account just for this purpose to avoid giving too broad access to the Server container project. But this is an easy way to get the integration to work.<\/p>\n<\/blockquote>\n<p>If you want to authenticate a <em>non-GCP<\/em> source to write to BigQuery, it gets quite a bit trickier. I recommend you follow the manual setup guide to do this.<\/p>\n<h2 id=\"summary\">Summary<\/h2>\n<p>You can ignore <strong>everything<\/strong> I\u2019ve written in this article and just rejoice about one single thing:<\/p>\n<p><strong>Server containers now have access to Google BigQuery!!!<\/strong><\/p>\n<p>I can\u2019t overstate how important this is. Being able to communicate with other cloud components without having to go through the motions of complicated service account bindings and authentication schemes is very nice. But being able to utilize one of the most powerful and disruptive data warehouses in existence is the real deal.<\/p>\n<p>With the BigQuery API, the sky is the limit for innovation.<\/p>\n<p>How about writing the incoming GA4 requests directly to Google BigQuery, instead of taking the roundtrip through Google Analytics?<\/p>\n<p>How about logging errors and other monitoring data into a BigQuery table for more detailed and cost-efficient analysis later?<\/p>\n<p>How about writing all transactions collected by Facebook into a BigQuery table for deduplication or debugging later?<\/p>\n<p>There\u2019s just <em>so much<\/em> you can do with Google BigQuery. I can\u2019t wait to see what folks in the template design community come up with.<\/p>\n<p>I hope that this is just foreshadowing the introduction of other Google Cloud APIs as well. Let\u2019s wish for a <strong>Cloud Storage<\/strong> API (for quick file read and write operations), and a <strong>Pub \/ Sub<\/strong> API (for intializing other cloud processes), for example.<\/p>\n<p>I also hope that BigQuery gets a <code>read<\/code> API as well, soon. Having <code>insert<\/code> capabilities is awesome, but if we could use BigQuery to actually enrich our Server container\u2019s data streams, it would make the setup even richer and more flexible.<\/p>\n<p>What do you think about the BigQuery API? What kinds of things could you envision being done with it?<\/p>\n<\/p><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Ever since it was released that server-side tagging in Google Tag Manager would run on the Google Cloud Platform stack, my imagination has been running wild. By running on GCP, the potential for integrations with other GCP components is limitless. The output to Cloud Logging already introduces interesting pipeline opportunities, but now it gets even [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":72372,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12033],"tags":[28608,1498,1531,32680,11062,4012],"dealstore":[],"offerexpiration":[],"class_list":["post-72371","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-bigquery","tag-container","tag-google","tag-gtm","tag-server","tag-write"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Write To Google BigQuery From A GTM Server Container - Som2ny Network<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fivemor.com\/?p=72371\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Write To Google BigQuery From A GTM Server Container - Som2ny Network\" \/>\n<meta property=\"og:description\" content=\"Ever since it was released that server-side tagging in Google Tag Manager would run on the Google Cloud Platform stack, my imagination has been running wild. By running on GCP, the potential for integrations with other GCP components is limitless. The output to Cloud Logging already introduces interesting pipeline opportunities, but now it gets even [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fivemor.com\/?p=72371\" \/>\n<meta property=\"og:site_name\" content=\"Som2ny Network\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-06T22:47:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1441\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fivemor.com\/?p=72371#article\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/?p=72371\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\"},\"headline\":\"Write To Google BigQuery From A GTM Server Container\",\"datePublished\":\"2025-02-06T22:47:13+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=72371\"},\"wordCount\":2915,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=72371#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg\",\"keywords\":[\"BigQuery\",\"Container\",\"Google\",\"GTM\",\"Server\",\"Write\"],\"articleSection\":[\"Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fivemor.com\/?p=72371#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fivemor.com\/?p=72371\",\"url\":\"https:\/\/fivemor.com\/?p=72371\",\"name\":\"Write To Google BigQuery From A GTM Server Container - Som2ny Network\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=72371#primaryimage\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=72371#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg\",\"datePublished\":\"2025-02-06T22:47:13+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fivemor.com\/?p=72371#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fivemor.com\/?p=72371\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/?p=72371#primaryimage\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg\",\"width\":2560,\"height\":1441},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fivemor.com\/?p=72371#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fivemor.com\/?bp_activities=1\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Write To Google BigQuery From A GTM Server Container\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fivemor.com\/#website\",\"url\":\"https:\/\/fivemor.com\/\",\"name\":\"Som2ny Network\",\"description\":\"Daily Deals\",\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fivemor.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/fivemor.com\/#organization\",\"name\":\"Som2ny Network\",\"url\":\"https:\/\/fivemor.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"width\":300,\"height\":86,\"caption\":\"Som2ny Network\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"caption\":\"admin\"},\"sameAs\":[\"https:\/\/fivemor.com\"],\"url\":\"https:\/\/fivemor.com\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Write To Google BigQuery From A GTM Server Container - Som2ny Network","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fivemor.com\/?p=72371","og_locale":"en_US","og_type":"article","og_title":"Write To Google BigQuery From A GTM Server Container - Som2ny Network","og_description":"Ever since it was released that server-side tagging in Google Tag Manager would run on the Google Cloud Platform stack, my imagination has been running wild. By running on GCP, the potential for integrations with other GCP components is limitless. The output to Cloud Logging already introduces interesting pipeline opportunities, but now it gets even [&hellip;]","og_url":"https:\/\/fivemor.com\/?p=72371","og_site_name":"Som2ny Network","article_published_time":"2025-02-06T22:47:13+00:00","og_image":[{"width":2560,"height":1441,"url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg","type":"image\/jpeg"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"16 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fivemor.com\/?p=72371#article","isPartOf":{"@id":"https:\/\/fivemor.com\/?p=72371"},"author":{"name":"admin","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371"},"headline":"Write To Google BigQuery From A GTM Server Container","datePublished":"2025-02-06T22:47:13+00:00","mainEntityOfPage":{"@id":"https:\/\/fivemor.com\/?p=72371"},"wordCount":2915,"commentCount":0,"publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"image":{"@id":"https:\/\/fivemor.com\/?p=72371#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg","keywords":["BigQuery","Container","Google","GTM","Server","Write"],"articleSection":["Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fivemor.com\/?p=72371#respond"]}]},{"@type":"WebPage","@id":"https:\/\/fivemor.com\/?p=72371","url":"https:\/\/fivemor.com\/?p=72371","name":"Write To Google BigQuery From A GTM Server Container - Som2ny Network","isPartOf":{"@id":"https:\/\/fivemor.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/fivemor.com\/?p=72371#primaryimage"},"image":{"@id":"https:\/\/fivemor.com\/?p=72371#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg","datePublished":"2025-02-06T22:47:13+00:00","breadcrumb":{"@id":"https:\/\/fivemor.com\/?p=72371#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fivemor.com\/?p=72371"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/?p=72371#primaryimage","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/write-to-google-big-query-from-a-gtm-server-container-scaled.jpg","width":2560,"height":1441},{"@type":"BreadcrumbList","@id":"https:\/\/fivemor.com\/?p=72371#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fivemor.com\/?bp_activities=1"},{"@type":"ListItem","position":2,"name":"Write To Google BigQuery From A GTM Server Container"}]},{"@type":"WebSite","@id":"https:\/\/fivemor.com\/#website","url":"https:\/\/fivemor.com\/","name":"Som2ny Network","description":"Daily Deals","publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fivemor.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/fivemor.com\/#organization","name":"Som2ny Network","url":"https:\/\/fivemor.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","width":300,"height":86,"caption":"Som2ny Network"},"image":{"@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371","name":"admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","caption":"admin"},"sameAs":["https:\/\/fivemor.com"],"url":"https:\/\/fivemor.com\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/72371","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=72371"}],"version-history":[{"count":0,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/72371\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/media\/72372"}],"wp:attachment":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=72371"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=72371"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=72371"},{"taxonomy":"dealstore","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fdealstore&post=72371"},{"taxonomy":"offerexpiration","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fofferexpiration&post=72371"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}