{"id":63490,"date":"2025-02-02T03:25:24","date_gmt":"2025-02-02T03:25:24","guid":{"rendered":"https:\/\/peraltafinancing.com\/analytics\/enrich-server-side-data-with-cloud-firestore\/"},"modified":"2025-02-02T03:25:24","modified_gmt":"2025-02-02T03:25:24","slug":"enrich-server-side-data-with-cloud-firestore","status":"publish","type":"post","link":"https:\/\/fivemor.com\/?p=63490","title":{"rendered":"Enrich Server-side Data With Cloud Firestore"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>With <a href=\"https:\/\/www.simoahava.com\/analytics\/asynchronous-variables-server-side-google-tag-manager\/\">asynchronous variables<\/a> recently released in <a href=\"https:\/\/developers.google.com\/tag-platform\/tag-manager\/server-side\">server-side Google Tag Manager<\/a>, it\u2019s time to dig into <strong>data enrichment flows<\/strong> using <em>another<\/em> release from the Google team.<\/p>\n<p><strong>* drum roll *<\/strong><\/p>\n<p>We have a new Google Cloud Platform API!<\/p>\n<p>It\u2019s fast. It\u2019s sleek. It\u2019s beautiful. It\u2019s <strong>Firestore<\/strong>!<\/p>\n<div style=\"aspect-ratio: 2140 \/ 554;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/cloud-firestore.jpg\" title=\"Firestore\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"554\" width=\"2140\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/cloud-firestore.jpg#ZgotmplZ\" alt=\"Firestore\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Firestore is a <strong>NoSQL<\/strong>, <strong>transactional<\/strong>, and <strong>scalable<\/strong> database that offers near-real-time write\/read and sync operations for data.<\/p>\n<p>In practice, it\u2019s a <em>great<\/em> way to enrich and widen the data that you pass through your Server container.<\/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=\"what-is-firestore\">What is Firestore<\/h2>\n<p>Firestore stores data in <strong>documents<\/strong>, each contained within a <strong>collection<\/strong>.<\/p>\n<div style=\"aspect-ratio: 2350 \/ 594;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-example.jpg\" title=\"Firestore example\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"594\" width=\"2350\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-example.jpg#ZgotmplZ\" alt=\"Firestore example\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Documents store data using familiar data types, such as <strong>strings<\/strong>, <strong>numbers<\/strong>, <strong>complex objects<\/strong>, and even <strong>subcollections<\/strong> for more hierarchical setups.<\/p>\n<p>When querying the data, you can either directly reference a document by its <strong>path<\/strong> (e.g. <code>order_history\/416cda47-cb46-435c-9c9a-15527d76343f<\/code>), or you can <strong>query<\/strong> the collection to fetch all documents that match the query (e.g. <code>iso_time == \"2022-02-10T09:21:27.718Z\"<\/code>).<\/p>\n<p>As such, Firestore has great utility for <em>widening<\/em> the data streams in the Server container.<\/p>\n<div style=\"aspect-ratio: 1704 \/ 686;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-widening.jpg\" title=\"Firestore widening\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"686\" width=\"1704\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-widening.jpg#ZgotmplZ\" alt=\"Firestore widening\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The incoming request can include a key, such as the <strong>transaction ID<\/strong>, and this key can then be used to query the Firestore for additional data.<\/p>\n<div style=\"aspect-ratio: 1774 \/ 1276;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-key.jpg\" title=\"Firestore Key\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1276\" width=\"1774\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-key.jpg#ZgotmplZ\" alt=\"Firestore Key\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Thanks to the new <a href=\"https:\/\/www.simoahava.com\/analytics\/asynchronous-variables-server-side-google-tag-manager\/\">asynchronous variables<\/a>, this can all be done quite easily (see below for examples).<\/p>\n<h2 id=\"the-new-firestore-lookup-variable\">The new Firestore Lookup variable<\/h2>\n<p>To make it easy to <strong>read<\/strong> Firestore data, there\u2019s a new <strong>Firestore Lookup<\/strong> variable in server-side Google Tag Manager.<\/p>\n<div style=\"aspect-ratio: 1788 \/ 1278;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-variable-blank.jpg\" title=\"Blank firestore variable\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1278\" width=\"1788\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-variable-blank.jpg#ZgotmplZ\" alt=\"Blank firestore variable\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>This variable lets you pull in the value from a specific <strong>key<\/strong>\/<strong>field<\/strong> in a Firestore <em>document<\/em>.<\/p>\n<h3 id=\"lookup-type\">Lookup Type<\/h3>\n<p>The <strong>Lookup Type<\/strong> specifies how the correct <strong>document<\/strong> is retrieved. If you recall, Firestore nests all the relevant information within a <em>document<\/em>, which is hosted in a <em>collection<\/em>.<\/p>\n<p>You can fetch the document directly using the full <strong>Document Path<\/strong>, or you can search for the document with <strong>Collection Path &amp; Query<\/strong>.<\/p>\n<p>Specifying the Document Path is the fastest method, because it\u2019s a direct lookup to fetch a single document.<\/p>\n<p>The document path typically looks like this:<\/p>\n<p><code>collection_name\/document_name<\/code><\/p>\n<div style=\"aspect-ratio: 1724 \/ 450;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/collection-document.jpg\" title=\"collection\/document\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"450\" width=\"1724\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/collection-document.jpg#ZgotmplZ\" alt=\"collection\/document\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>For example, to fetch the first document in the screenshot above, I would specify this as the Document Path:<\/p>\n<p><code>order_history\/416cda47-cb46-435c-9c9a-15527d76343f<\/code><\/p>\n<p>Alternatively, if you can\u2019t access the document directly with the path, you can also <strong>query<\/strong> it. You first need to provide a path to the collection that has the document. Then, you can add one or more <strong>query conditions<\/strong> using keys you know to exist in the document.<\/p>\n<div style=\"aspect-ratio: 1760 \/ 558;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/user-query.jpg\" title=\"User Query\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"558\" width=\"1760\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/user-query.jpg#ZgotmplZ\" alt=\"User Query\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The query above would look for documents embedded within the <code>user_database<\/code> collection. The variable would fetch the <strong>first<\/strong> document that has the <code>user_id<\/code> field set to whatever is contained in the GTM <code>{{User ID}}<\/code> variable (could be something from the incoming request, for example) and has the <code>user_status<\/code> field set to something other than <code>inactive<\/code>.<\/p>\n<blockquote>\n<p>Because Firestore indexes documents by default, query performance is proportional to the size of the <strong>result set<\/strong> and not the data set being queried. So it doesn\u2019t matter how many documents there are in the collection for query performance, but it does matter how broad your query is (i.e. how many results it retrieves).<\/p>\n<\/blockquote>\n<p>The Firestore variable uses the <strong>first document that matches the query<\/strong> for returning the key (see below). This is something to consider in case your query is too general and returns multiple (unrelated) documents.<\/p>\n<h3 id=\"key-path\">Key Path<\/h3>\n<p>Assuming the lookup returns a document, the <strong>Key Path<\/strong> then specifies which value to fetch.<\/p>\n<p>Here\u2019s a dummy example of a Firestore document:<\/p>\n<div style=\"aspect-ratio: 866 \/ 954;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/dummy-keys.jpg\" title=\"Dummy document\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"954\" width=\"866\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/dummy-keys.jpg#ZgotmplZ\" alt=\"Dummy document\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>To fetch the <code>transaction_id<\/code>, I would set that as the <strong>Key Path<\/strong>. To fetch the <code>value<\/code>, I would set that. To fetch the ID of the first item, I could use <code>items.0.item_id<\/code>, and so on.<\/p>\n<p>If no value is found, then the <strong>Default Value<\/strong> is returned if set, or <code>undefined<\/code> otherwise.<\/p>\n<h3 id=\"override-project-id\">Override Project ID<\/h3>\n<p>By default, the variable looks for collections and documents in the <strong>same Google Cloud Project<\/strong> that runs the GTM server.<\/p>\n<blockquote>\n<p><strong>Note!<\/strong> This requires that the Project ID is set in the <code>GOOGLE_CLOUD_PROJECT<\/code> environment variable of the cloud service. This is done automatically in the App Engine deployment. If you\u2019re running <a href=\"https:\/\/www.simoahava.com\/gtmtips\/setup-google-tag-manager-server-cloud-run\/\">Cloud Run<\/a> or some other stack, you need to add the environment variable to the service <em>or<\/em> you can simply type the project ID into the Project ID field.<\/p>\n<\/blockquote>\n<p>If you want, you can <strong>override the Project ID<\/strong> and provide some other Google Cloud Project ID where the collection is stored.<\/p>\n<p>To do this, expand <strong>More Settings<\/strong> in the tag and check <strong>Override Project ID<\/strong>.<\/p>\n<div style=\"aspect-ratio: 934 \/ 298;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/override-project-id.jpg\" title=\"Override Project ID\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"298\" width=\"934\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/override-project-id.jpg#ZgotmplZ\" alt=\"Override Project ID\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<blockquote>\n<p>If you provide another Project ID, you must add your GTM Server project <strong>service account<\/strong> to the Firestore project via IAM.<\/p>\n<\/blockquote>\n<div style=\"aspect-ratio: 2210 \/ 412;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/iam-roles.jpg\" title=\"IAM roles\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"412\" width=\"2210\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/iam-roles.jpg#ZgotmplZ\" alt=\"IAM roles\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>If you\u2019re running your container on App Engine, locate the <strong>App Engine default service account<\/strong> in your <a href=\"https:\/\/console.cloud.google.com\/iam-admin\/serviceaccounts\">GTM Server project IAM<\/a>, and add it as a <a href=\"https:\/\/console.cloud.google.com\/iam-admin\/iam\">new role into your Firestore project<\/a>.<\/p>\n<p>If your container is running on Cloud Run, use the <strong>Compute Engine default service account<\/strong> instead.<\/p>\n<p>Grant the service account a role of <strong>Cloud Datastore User<\/strong> to give the GTM Server access to the other project\u2019s Firestore.<\/p>\n<h2 id=\"example-walkthrough\">Example walkthrough<\/h2>\n<p>In this fictitious example, I\u2019ll show you how you could use Firestore to <strong>widen<\/strong> an incoming <strong>Google Analytics 4<\/strong> data stream.<\/p>\n<div style=\"aspect-ratio: 4588 \/ 2674;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/example-process.jpg\" title=\"Example process\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"2674\" width=\"4588\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/example-process.jpg#ZgotmplZ\" alt=\"Example process\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>As you can see from the immaculately designed process flow above, there are several moving parts in this setup.<\/p>\n<ol>\n<li>First, the user <strong>makes a purchase<\/strong> on the website.<\/li>\n<li>As soon as the purchase happens, an entry is made into the <strong>local database<\/strong> (or service database) of the store.<\/li>\n<li>Immediately, the order details are written into a <strong>Firebase collection<\/strong>, too.<\/li>\n<li>The web server responds to the site that the purchase was successful, at which point the site sends a <strong>Google Analytics 4<\/strong> event to the <strong>server-side Google Tag Manager endpoint<\/strong>, with the <strong>Transaction ID<\/strong> as one of the parameters.<\/li>\n<li>The server-side endpoint then takes this Transaction ID from the request and uses <strong>Firestore variables<\/strong> to pull the rest of the purchase information.<\/li>\n<li>These variables are then added to the server-side Google Analytics tag (and other tags that need this information), in order to complete the required parameters that a well-formed <strong>Purchase event<\/strong> requires.<\/li>\n<\/ol>\n<p>Because this is a made-up example, I\u2019m going to skip steps <strong>1\u20133<\/strong>. It\u2019s a bit of a cop-out because engineering the data flow from the sales engine to Firestore isn\u2019t trivial to do. It has to happen <strong>immediately<\/strong> to avoid latency and potential race conditions, where the server container tries to query for data that doesn\u2019t (yet) exist.<\/p>\n<h3 id=\"firestore-collection\">Firestore collection<\/h3>\n<p>In Firestore, the collection ends up looking like this:<\/p>\n<div style=\"aspect-ratio: 2856 \/ 1108;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/transaction-ids.jpg\" title=\"Transaction IDs\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1108\" width=\"2856\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/transaction-ids.jpg#ZgotmplZ\" alt=\"Transaction IDs\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>There\u2019s a collection called <code>order_history<\/code>, under which each transaction is its own, unique document. I\u2019m using the Transaction ID as the name of each document to make the lookup easier.<\/p>\n<p>Each transaction document has the following fields. I\u2019ve chosen these fields and these types to require as little transformation in the Server container as possible. Thus, the fields and the values follow closely what <a href=\"https:\/\/www.simoahava.com\/analytics\/google-analytics-4-ecommerce-guide-google-tag-manager\/\">GA4\u2019s ecommerce schema<\/a> expects.<\/p>\n<ul>\n<li><code>currency<\/code> &#8211; the local currency of the purchase (e.g. <code>EUR<\/code>).<\/li>\n<li><code>iso_time<\/code> &#8211; timestamp of when the order happened (for convenience).<\/li>\n<li><code>items<\/code> &#8211; an array of all the items included in the transaction.<\/li>\n<li><code>shipping<\/code> &#8211; shipping cost (e.g. <code>1.99<\/code>).<\/li>\n<li><code>tax<\/code> &#8211; how much tax was paid (e.g. <code>1.24<\/code>).<\/li>\n<li><code>transaction_id<\/code> &#8211; the order ID (duplicate of the document name).<\/li>\n<li><code>value<\/code> &#8211; the total value of the purchase.<\/li>\n<\/ul>\n<h3 id=\"server-side-gtm-setup\">Server-side GTM setup<\/h3>\n<p>In server-side GTM, I\u2019ve got a Google Analytics 4 <strong>event tag<\/strong> set to fire when the event with the Transaction ID comes in.<\/p>\n<p>The Transaction ID is sent as a <strong>custom event parameter<\/strong> with the name <code>custom_ti<\/code>. Thus, I can create a <strong>Query Parameter<\/strong> variable for it like this:<\/p>\n<div style=\"aspect-ratio: 1782 \/ 624;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/query-parameter.jpg\" title=\"Query parameter\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"624\" width=\"1782\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/query-parameter.jpg#ZgotmplZ\" alt=\"Query parameter\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>This variable returns the value of the <code>&amp;ep.custom_ti<\/code> parameter in the incoming GA4 request.<\/p>\n<p>My Google Analytics 4 tag in the Server container then looks like this:<\/p>\n<div style=\"aspect-ratio: 1758 \/ 1768;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/ga4-tag-in-sgtm.jpg\" title=\"GA4 tag in SGTM\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1768\" width=\"1758\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/ga4-tag-in-sgtm.jpg#ZgotmplZ\" alt=\"GA4 tag in SGTM\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Here, you can see that I hard-code the <strong>event name<\/strong> as <code>purchase<\/code>. Then, all the fields that GA4 expects to be included in a Purchase hit are added to the tag.<\/p>\n<p>The <code>transaction_id<\/code> is set to point to the custom parameter in the incoming request, and all the other fields are <strong>Firestore<\/strong> variables.<\/p>\n<p>Each Firestore variable points to the relevant key in the order document. Here\u2019s how the variables are configured:<\/p>\n<div style=\"aspect-ratio: 1440 \/ 716;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-variable-example-ga4.jpg\" title=\"Firestore Variable example\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"716\" width=\"1440\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-variable-example-ga4.jpg#ZgotmplZ\" alt=\"Firestore Variable example\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>As you can see, I\u2019m using the <code>{{Request - ep.custom_ti}}<\/code> variable (which takes the Transaction ID from the request URL) to establish the <strong>Document Path<\/strong> (with the <code>order_history<\/code> collection name hard-coded).<\/p>\n<p><em>This<\/em> is why I set the Transaction ID as the document name when creating the Firestore collection. I can do direct lookups rather than having to make queries.<\/p>\n<p>If I <em>did<\/em> want to make a query, this is how I could fetch the correct document:<\/p>\n<div style=\"aspect-ratio: 1746 \/ 782;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-query-lookup.jpg\" title=\"Firestore Query Lookup\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"782\" width=\"1746\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-query-lookup.jpg#ZgotmplZ\" alt=\"Firestore Query Lookup\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>This would fetch the first document that has the <code>transaction_id<\/code> field with the same value as the <code>ep.custom_ti<\/code> parameter in the incoming request.<\/p>\n<h3 id=\"outcome\">Outcome<\/h3>\n<p>When testing this, I\u2019ll first need to check that the incoming request looks good:<\/p>\n<div style=\"aspect-ratio: 2076 \/ 1006;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/request-to-server-container.jpg\" title=\"Request to server container\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1006\" width=\"2076\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/request-to-server-container.jpg#ZgotmplZ\" alt=\"Request to server container\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Yes, there\u2019s our <code>ep.custom_ti<\/code> parameter! The event name is <code>custom_purchase<\/code> but this is irrelevant because we hard-code it to <code>purchase<\/code> in the Server container.<\/p>\n<p>Now, let\u2019s take a look at all the outgoing network calls that the Server container makes when the incoming request is processed:<\/p>\n<div style=\"aspect-ratio: 4402 \/ 1180;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/five-firestore-calls.jpg\" title=\"Five firestore calls\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1180\" width=\"4402\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/five-firestore-calls.jpg#ZgotmplZ\" alt=\"Five firestore calls\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Uhh, that\u2019s quite a few Firestore calls. Well, there\u2019s one for each key that is fetched from Firestore. Luckily they all return <strong>200<\/strong> status.<\/p>\n<p>I\u2019ll then select the <code>custom_purchase<\/code> event and check what my variables return.<\/p>\n<div style=\"aspect-ratio: 1702 \/ 352;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-variables.jpg\" title=\"Firestore variables\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"352\" width=\"1702\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/firestore-variables.jpg#ZgotmplZ\" alt=\"Firestore variables\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Looking <em>very<\/em> good! All of the variables have values, and these values match what was written into the Firebase document.<\/p>\n<p>Finally, let\u2019s see what the GA4 tag sent to Google servers.<\/p>\n<div style=\"aspect-ratio: 1544 \/ 1360;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/ga4-tag-ss.jpg\" title=\"GA4 tag\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1360\" width=\"1544\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/ga4-tag-ss.jpg#ZgotmplZ\" alt=\"GA4 tag\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p><strong>Yes!<\/strong> All the fields are set correctly. OK, let\u2019s quickly peek at the outbound request, too.<\/p>\n<div style=\"aspect-ratio: 1802 \/ 968;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2022\/02\/parameters-in-outgoing-request.jpg\" title=\"Parameters in the outgoing request\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"968\" width=\"1802\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2022\/02\/parameters-in-outgoing-request.jpg#ZgotmplZ\" alt=\"Parameters in the outgoing request\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>And there you have it. If you recall, I only sent the <strong>transaction ID<\/strong> to the server. It was widened with data from the Firestore document, and the final request to Google servers was formatted as a proper purchase event.<\/p>\n<p><strong>This all was done without having to create <em>any<\/em> custom Client, tag, or variable templates.<\/strong><\/p>\n<blockquote>\n<p>Obviously, this isn\u2019t the most useful example, because typically you\u2019d include the transaction data already in the client. But as an example of how data widening works, I think it\u2019s a pretty nifty setup.<\/p>\n<\/blockquote>\n<h2 id=\"the-new-template-apis\">The new template APIs<\/h2>\n<p>In addition to the Firestore variable, a new template API, <code>Firestore<\/code>, has been released. All its methods are <a href=\"https:\/\/www.simoahava.com\/analytics\/asynchronous-variables-server-side-google-tag-manager\/\">Promise-friendly<\/a>, of course.<\/p>\n<ul>\n<li><code>Firestore.read<\/code> \u2013\u00a0use this to fetch a <strong>document<\/strong> or <strong>collection<\/strong> from Firestore with a direct lookup.<\/li>\n<li><code>Firestore.write<\/code> &#8211; use this to write into a <strong>document<\/strong> in Firestore. If the document doesn\u2019t exist, it\u2019s automatically created in the collection.<\/li>\n<li><code>Firestore.query<\/code> &#8211; use this to fetch <strong>document<\/strong>(s) that match the given query.<\/li>\n<li><code>Firestore.runTransaction<\/code> &#8211; use this to run batched and atomic read\/write operations.<\/li>\n<\/ul>\n<p>As an example, here\u2019s what a <code>Firestore.query<\/code> call would look like:<\/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:#00a\">const<\/span> Firestore = require(<span style=\"color:#a50\">'Firestore'<\/span>);\n\n<span style=\"color:#00a\">const<\/span> queries = [[<span style=\"color:#a50\">'user_id'<\/span>, <span style=\"color:#a50\">'=='<\/span>, <span style=\"color:#a50\">'admin'<\/span>], [<span style=\"color:#a50\">'country'<\/span>, <span style=\"color:#a50\">'=='<\/span>, <span style=\"color:#a50\">'FI'<\/span>]];\n\n<span style=\"color:#00a\">return<\/span> Firestore.query(<span style=\"color:#a50\">'users'<\/span>, queries, {\n  projectId: <span style=\"color:#a50\">'my-project'<\/span>,\n  limit: <span style=\"color:#099\">1<\/span>,\n}).then((documents) =&gt; {\n  <span style=\"color:#00a\">return<\/span> documents;\n});\n<\/code><\/pre>\n<\/div>\n<p>This queries the collection <code>users<\/code> for a document (one, because <code>limit<\/code> is <code>1<\/code>), which has the <code>user_id<\/code> key set to <code>admin<\/code> and the <code>country<\/code> key set to <code>FI<\/code>.<\/p>\n<p>If successful, an array of matching documents (again, just one) is returned by the callback.<\/p>\n<p>Note that for this to work in a variable, the API call itself must be returned (<code>return Firestore.query...<\/code>), as it returns a Promise that GTM\u2019s runtime code will then handle appropriately.<\/p>\n<p><code>runTransaction<\/code> might sound confusing, but it\u2019s a useful way of having a set of <strong>write<\/strong> operations immediately following a related set of <strong>read<\/strong> operations. This is handy if the <em>write<\/em> requires an input from the <em>read<\/em>. The <em>atomicity<\/em> refers to the fact that either <strong>all operations are a success<\/strong> or <strong>all operations fail<\/strong>. In case of failure, up to <strong>two<\/strong> retries are automatically done. If three total attempts fail, the API rejects the Promise with an error.<\/p>\n<blockquote>\n<p>Transactions are a way of protecting your read-write setups from other, <strong>concurrent<\/strong> write operations. For example, while reading the data from a Firestore document, if some other process modifies this data, then the transaction automatically starts over to ensure that it runs on the most recent and up-to-date data.<\/p>\n<\/blockquote>\n<p>Read more about these APIs here.<\/p>\n<h2 id=\"summary\">Summary<\/h2>\n<p>Firestore might be more obscure for \u201ccasual\u201d Google Tag Manager users, at least when compared to something like Google BigQuery.<\/p>\n<p>However, while BigQuery is a great <strong>data warehouse<\/strong>, it\u2019s not a <strong>transactional<\/strong> database, as its <em>read<\/em> operations can\u2019t handle the type of low latency and cost that a NoSQL, real-time database like Firestore can.<\/p>\n<p>The opportunities for enriching data are limitless. Naturally, you need to have a plan in place for populating those Firestore documents, but for data widening purposes you could do things like\u2026<\/p>\n<ul>\n<li>Build a hash table of users\u2019 first-party identifiers to make sure that what\u2019s stored on their device isn\u2019t what\u2019s sent to the vendors.<\/li>\n<li>Temporarily cache information that needs to be available to other requests in the Server container, too. This is more deterministic than what <a href=\"https:\/\/developers.google.com\/tag-platform\/tag-manager\/server-side\/api#templatedatastorage\"><code>templateDataStorage<\/code><\/a> offers.<\/li>\n<li>Build a simple real-time analytics system where the Server container <strong>writes<\/strong> the hit stream data and a dashboard <strong>reads<\/strong> it.<\/li>\n<li>Build a real-time monitor to quickly address potential data collection issues.<\/li>\n<li>And of course the million different use cases for enriching data using an input key from the incoming request, similar to what we did in this article.<\/li>\n<\/ul>\n<p>I\u2019m excited at having another, extremely useful Google Cloud API to play with in the Server container.<\/p>\n<p>What do you think about Firestore? Can you think of other, cool use cases for the API?<\/p>\n<\/p><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>With asynchronous variables recently released in server-side Google Tag Manager, it\u2019s time to dig into data enrichment flows using another release from the Google team. * drum roll * We have a new Google Cloud Platform API! It\u2019s fast. It\u2019s sleek. It\u2019s beautiful. It\u2019s Firestore! Firestore is a NoSQL, transactional, and scalable database that offers [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":63491,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12033],"tags":[12564,11603,34656,34657,18475],"dealstore":[],"offerexpiration":[],"class_list":["post-63490","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-cloud","tag-data","tag-enrich","tag-firestore","tag-serverside"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Enrich Server-side Data With Cloud Firestore - 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=63490\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Enrich Server-side Data With Cloud Firestore - Som2ny Network\" \/>\n<meta property=\"og:description\" content=\"With asynchronous variables recently released in server-side Google Tag Manager, it\u2019s time to dig into data enrichment flows using another release from the Google team. * drum roll * We have a new Google Cloud Platform API! It\u2019s fast. It\u2019s sleek. It\u2019s beautiful. It\u2019s Firestore! Firestore is a NoSQL, transactional, and scalable database that offers [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fivemor.com\/?p=63490\" \/>\n<meta property=\"og:site_name\" content=\"Som2ny Network\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-02T03:25:24+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-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=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fivemor.com\/?p=63490#article\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/?p=63490\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\"},\"headline\":\"Enrich Server-side Data With Cloud Firestore\",\"datePublished\":\"2025-02-02T03:25:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=63490\"},\"wordCount\":2228,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=63490#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg\",\"keywords\":[\"Cloud\",\"Data\",\"Enrich\",\"Firestore\",\"Serverside\"],\"articleSection\":[\"Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fivemor.com\/?p=63490#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fivemor.com\/?p=63490\",\"url\":\"https:\/\/fivemor.com\/?p=63490\",\"name\":\"Enrich Server-side Data With Cloud Firestore - Som2ny Network\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=63490#primaryimage\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=63490#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg\",\"datePublished\":\"2025-02-02T03:25:24+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fivemor.com\/?p=63490#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fivemor.com\/?p=63490\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/?p=63490#primaryimage\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg\",\"width\":2560,\"height\":1441},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fivemor.com\/?p=63490#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fivemor.com\/?bp_activities=1\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Enrich Server-side Data With Cloud Firestore\"}]},{\"@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":"Enrich Server-side Data With Cloud Firestore - 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=63490","og_locale":"en_US","og_type":"article","og_title":"Enrich Server-side Data With Cloud Firestore - Som2ny Network","og_description":"With asynchronous variables recently released in server-side Google Tag Manager, it\u2019s time to dig into data enrichment flows using another release from the Google team. * drum roll * We have a new Google Cloud Platform API! It\u2019s fast. It\u2019s sleek. It\u2019s beautiful. It\u2019s Firestore! Firestore is a NoSQL, transactional, and scalable database that offers [&hellip;]","og_url":"https:\/\/fivemor.com\/?p=63490","og_site_name":"Som2ny Network","article_published_time":"2025-02-02T03:25:24+00:00","og_image":[{"width":2560,"height":1441,"url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg","type":"image\/jpeg"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fivemor.com\/?p=63490#article","isPartOf":{"@id":"https:\/\/fivemor.com\/?p=63490"},"author":{"name":"admin","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371"},"headline":"Enrich Server-side Data With Cloud Firestore","datePublished":"2025-02-02T03:25:24+00:00","mainEntityOfPage":{"@id":"https:\/\/fivemor.com\/?p=63490"},"wordCount":2228,"commentCount":0,"publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"image":{"@id":"https:\/\/fivemor.com\/?p=63490#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg","keywords":["Cloud","Data","Enrich","Firestore","Serverside"],"articleSection":["Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fivemor.com\/?p=63490#respond"]}]},{"@type":"WebPage","@id":"https:\/\/fivemor.com\/?p=63490","url":"https:\/\/fivemor.com\/?p=63490","name":"Enrich Server-side Data With Cloud Firestore - Som2ny Network","isPartOf":{"@id":"https:\/\/fivemor.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/fivemor.com\/?p=63490#primaryimage"},"image":{"@id":"https:\/\/fivemor.com\/?p=63490#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg","datePublished":"2025-02-02T03:25:24+00:00","breadcrumb":{"@id":"https:\/\/fivemor.com\/?p=63490#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fivemor.com\/?p=63490"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/?p=63490#primaryimage","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/export-server-side-data-cloud-firestore-scaled.jpg","width":2560,"height":1441},{"@type":"BreadcrumbList","@id":"https:\/\/fivemor.com\/?p=63490#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fivemor.com\/?bp_activities=1"},{"@type":"ListItem","position":2,"name":"Enrich Server-side Data With Cloud Firestore"}]},{"@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\/63490","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=63490"}],"version-history":[{"count":0,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/63490\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/media\/63491"}],"wp:attachment":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=63490"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=63490"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=63490"},{"taxonomy":"dealstore","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fdealstore&post=63490"},{"taxonomy":"offerexpiration","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fofferexpiration&post=63490"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}