{"id":27797768,"url":"https://github.com/kabuswe/ga4-demo","last_synced_at":"2026-05-09T05:33:19.613Z","repository":{"id":290596446,"uuid":"971275555","full_name":"Kabuswe/GA4-DEMO","owner":"Kabuswe","description":"Secure backend proxy for querying Google Analytics 4 (GA4) data via the Data API using a service account.","archived":false,"fork":false,"pushed_at":"2025-04-29T15:56:23.000Z","size":4,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-29T16:48:43.932Z","etag":null,"topics":["analytics-ap","api-proxy","data-reporting","express","ga4-api","google-analytics-api","node","service-account"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kabuswe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-23T09:20:45.000Z","updated_at":"2025-04-29T15:56:27.000Z","dependencies_parsed_at":"2025-04-29T16:58:50.975Z","dependency_job_id":null,"html_url":"https://github.com/Kabuswe/GA4-DEMO","commit_stats":null,"previous_names":["kabuswe/ga4-demo"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kabuswe%2FGA4-DEMO","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kabuswe%2FGA4-DEMO/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kabuswe%2FGA4-DEMO/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kabuswe%2FGA4-DEMO/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kabuswe","download_url":"https://codeload.github.com/Kabuswe/GA4-DEMO/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251791982,"owners_count":21644495,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["analytics-ap","api-proxy","data-reporting","express","ga4-api","google-analytics-api","node","service-account"],"created_at":"2025-04-30T22:30:33.898Z","updated_at":"2025-10-06T18:06:51.020Z","avatar_url":"https://github.com/Kabuswe.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 📘 GA4 Proxy Integration Wiki\n\n## 🔧 Project Overview\n\nThis project sets up a **backend proxy for Google Analytics 4 (GA4) reporting**, using a **Node.js + Express** server. The backend securely interacts with the GA4 Data API via a **service account**, allowing frontend clients or tools like Postman to request analytics reports using custom filters, dimensions, and metrics.\n\n---\n\n## 🛠️ Backend Configuration\n\n### ✅ Technologies Used\n\n- **Node.js** / **Express**\n- `@google-analytics/data` (GA4 API SDK)\n- `google-auth-library` (for service account auth)\n\n### ✅ Required Files\n\n- `index.js` → Express server\n- `service-account.json` → Downloaded from GCP (contains private key, email, etc.)\n- `package.json` → Node dependencies\n\n### ✅ Service Account Setup\n\n1. **Create a service account** in [Google Cloud Console](https://console.cloud.google.com/iam-admin/serviceaccounts).\n2. **Generate a JSON key file** for this service account.\n3. **Grant the account GA4 Viewer access**:\n   - Go to **GA4 Admin → Property Access Management**\n   - Add the service account email with **Viewer** role\n4. Place the `.json` key file in the root directory (`/GA4-DEMO`)\n\n### ✅ GA4 Configuration\n\n1. **Custom Dimensions Setup**\n   - Register any used dimensions (e.g., `platform_portal`)\n   - Scope: `Event`, `User`, or `Item` depending on intended use\n   - Parameter name must match what’s sent in the correct scope (event param, user property, or item param)\n\n2. **Event Collection Validation**\n   - Ensure data is being sent with `gtag()` or GTM\n   - Confirm data exists in recent date ranges (use Free Form UI)\n\n---\n\n## 🧾 API Usage\n\n### ✅ API Endpoint\n\n```bash\nPOST /report\n```\n\n### ✅ Request Payload Format\n\n```json\n{\n  \"startDate\": \"7daysAgo\",\n  \"endDate\": \"today\",\n  \"dimensions\": [\"customEvent:platform_portal\", \"city\", \"deviceCategory\"],\n  \"metrics\": [\"eventCount\", \"activeUsers\"],\n  \"filters\": [\n    { \"field\": \"city\", \"value\": \"Casablanca\" },\n    { \"field\": \"deviceCategory\", \"value\": \"desktop\" }\n  ]\n}\n```\n\n### ✅ Supported Options\n\n- `dimensions[]` – GA4 dimensions (registered if custom)\n- `metrics[]` – GA4 metrics (e.g., `eventCount`, `activeUsers`)\n- `filters[]` – array of dimension-level filters\n- `orderBys[]` – optional metric sort (planned support)\n\n---\n\n## ⚙️ Functional Flow\n\n### ✅ Report Handling Logic\n\n1. Client sends a `POST /report` request with JSON payload\n2. Backend reads:\n   - `startDate`, `endDate`\n   - `dimensions[]`, `metrics[]`\n   - Optional filters\n3. Backend uses `BetaAnalyticsDataClient.runReport()` to query GA4\n4. Response is returned to client in raw GA4 API format\n5. Client (frontend/Postman) reshapes or renders the data\n\n---\n\n## 🔐 Security Notes\n\n- `service-account.json` should never be committed or exposed\n- Use `.env` or secret management in production\n- Whitelist allowed dimensions/metrics to prevent misuse\n\n---\n\n## 📊 Use Case Flow Diagram (Plain-Text)\n\n```plaintext\n +------------------+           +-----------------------+            +------------------------------+\n |   Frontend App   |           |     Backend Proxy     |            |         GA4 Data API         |\n | (or Postman Req) |  POST     |  Express + Service Acc|   Auth +   |    analyticsdata.googleapis  |\n +--------+---------+ --------\u003e +-----------+-----------+ ---------\u003e +--------------+---------------+\n          |                                 |                                 |\n          |  JSON payload:                  |  Validates dimensions,          |\n          |  - startDate, endDate           |  filters, and metrics           |\n          |  - dimensions, metrics          |                                 |\n          |  - optional filters             |  Generates GA4 API request      |\n          |                                 |                                 |\n          |                                 |  Fetches GA4 report             |\n          |                                 | \u003c------------------------------+\n          |                                 |                                 |\n          |  Receives report JSON           |                                 |\n          | \u003c-------------------------------+                                 |\n          |  Displays, reshapes, visualizes |                                 |\n +--------+---------+                       |                                 |\n |        Client     |                       |                                 |\n +------------------+                       +---------------------------------+\n```\n\n---\n\n## ✅ Supported Use Cases\n\n| Use Case | Description |\n|----------|-------------|\n| 💻 Dashboarding | Dynamically fetch GA4 metrics from different dimensions |\n| 🧪 Postman Testing | Manual analytics report exploration |\n| 🧑‍💼 Client-specific filtering | Filter results by platform, city, or custom identifiers |\n| 📊 Pivot-style tables | Client-side grouping by `deviceCategory`, `platform_portal`, etc. |\n| 🔄 Caching (future) | Optimize repeated queries with in-memory or Redis cache |\n| 🔒 Role-based field control | Lock down dimensions/metrics per client or session (planned) |\n\n---\n\n## 🎯 Dimension-Level Filtering Methods\n\nThe backend supports advanced dimension-level filtering using the GA4 Data API’s `dimensionFilter` field. Supported methods include:\n\n### 1. Single Filter (Default)\n\n```json\n\"filters\": [\n  { \"field\": \"city\", \"value\": \"Casablanca\" }\n]\n```\n\n### 2. Multi-Filter with AND Logic\n\n```json\n\"filters\": [\n  { \"field\": \"city\", \"value\": \"Casablanca\" },\n  { \"field\": \"deviceCategory\", \"value\": \"desktop\" }\n]\n```\n\n### 3. Match Type Variants\n\n```json\n\"filters\": [\n  { \"field\": \"city\", \"value\": \"casa\", \"matchType\": \"CONTAINS\", \"caseSensitive\": false }\n]\n```\n\n| Match Type     | Description                |\n|----------------|----------------------------|\n| `EXACT`        | Exact string match (default) |\n| `BEGINS_WITH`  | Value starts with filter    |\n| `CONTAINS`     | Value contains filter       |\n\nYou can combine any number of these filters and the backend will construct a GA4-compatible `andGroup` dimension filter block.\n\n---\n\n## 🔧 Querying Custom Dimensions\n\nTo successfully query custom dimensions, you must use the correct prefix depending on the dimension's scope:\n\n### ✅ Syntax by Scope\n\n| Scope        | API Format Example                       |\n|--------------|-------------------------------------------|\n| Event        | `customEvent:platform_portal`             |\n| User         | `customUser:user_type`                    |\n| Item         | `customItem:item_category`                |\n\n### ✅ Request Example\n\n```json\n\"dimensions\": [\"customEvent:platform_portal\", \"customUser:membership_status\", \"customItem:category_name\"]\n```\n\n### ✅ Backend Behavior\n\n- Native dimensions (like `city`, `deviceCategory`) are passed unchanged\n- Custom dimensions are auto-prefixed with the appropriate scope if not already present\n- By default, the backend assumes `customEvent:` for unknown fields unless configured otherwise\n\nTo override this, pass explicitly prefixed dimension names or configure scope handling logic inside the backend.\n\n\u003e You can confirm available custom dimensions via the [GA4 Metadata API](https://developers.google.com/analytics/devguides/reporting/data/v1/metadata-api), which returns all valid dimensions and metrics for a given property.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkabuswe%2Fga4-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkabuswe%2Fga4-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkabuswe%2Fga4-demo/lists"}