A full-stack reference implementation showing how to build a customer-facing
React UI on top of Google Cloud's Looker Conversational Analytics API
(geminidataanalytics.googleapis.com, GA Nov 2025).
It includes:
- React + Vite + TypeScript frontend
- Home / feature tour
- Configure page (ADC, GCP project, Looker host + API3 creds, embed secret, Explores)
- Dashboards list — embedded via signed Looker URLs
- Chat — multi-turn conversation with SQL/source inspection
- Explores picker — choose up to 5 Looker Explores as the agent's grounding
- Side-panel chat on every embedded dashboard (shared filter context)
- Express proxy backend (
/server)- Looker SDK (
@looker/sdk) forme, dashboards, explores - HMAC-SHA1 signed embed URL generator
google-auth-libraryfor ADC → access tokens- Proxies to
POST /v1beta/projects/{p}/locations/{l}:chat
- Looker SDK (
# 1. install
cd looker-api
npm install
# 2. application default credentials (one-time)
gcloud auth application-default login
# 3. (optional) seed defaults via env
cp .env.example .env
# 4. dev — runs Vite (5173) + Express (8787) concurrently
npm run devOpen http://localhost:5173/configure first. Fill in:
| Field | Where to find it |
|---|---|
| GCP project ID | Cloud console — project picker |
| Service-account key path (optional) | If you don't use gcloud auth application-default login |
| BigQuery dataset / table | Whichever dataset your Looker model points at |
| Looker base URL | https://yourco.cloud.looker.com |
| Looker client ID / secret | Looker → Admin → Users → Edit Keys (API3 credentials) |
| Embed secret (optional) | Looker → Admin → Embed |
| Explores | Click "Load Explores from Looker", then pick ≤ 5 |
Hit Test connection. You should see three green rows: ADC, GCP project, Looker SDK.
geminidataanalytics.googleapis.combigquery.googleapis.comcloudaicompanion.googleapis.com
IAM roles for the principal (service account or your user via ADC):
roles/bigquery.dataViewerroles/bigquery.userroles/looker.instanceUser
React (Vite, :5173)
├── Looker Embed SDK / signed iframe → renders dashboards
├── ChatPanel → POST /api/chat/turn
│
Express proxy (:8787)
├── /api/config → on-disk JSON at ~/.looker-api-app/config.json
├── /api/looker/* → Looker SDK calls (dashboards, explores, embed-url)
└── /api/chat/turn → fetch(geminidataanalytics.../v1beta/{parent}:chat,
Authorization: Bearer $(google-auth ADC))
The chat endpoint is stateless on the server: each turn ships the prior
history along with the user's new message and the selected Explores as
inline_context.datasource_references.looker.explore_references. That keeps
the demo simple — if you want persistent agents, switch to creating a
DataAgent resource and call :chat against it.
looker-api/
├── server/ Express + Looker SDK + ADC + CA proxy
│ ├── index.js
│ ├── routes/{config,looker,chat}.js
│ └── services/{configStore,looker,conversational}.js
├── src/
│ ├── main.tsx · App.tsx
│ ├── components/{Layout,ChatPanel}.tsx
│ ├── context/ConfigContext.tsx
│ ├── pages/{Home,Configure,Dashboards,DashboardDetail,Chat,Explore}.tsx
│ ├── services/api.ts
│ ├── styles/global.css
│ └── types/index.ts
├── index.html · vite.config.ts · tsconfig.json
└── package.json
- Secrets: this demo writes Looker client secret & embed secret to a JSON
file under
~/.looker-api-app/. Move to Secret Manager / KMS before shipping. - Auth: front the proxy with your real session middleware. The signed
embed URL builder takes a
userobject — wire it to your authenticated session. - Streaming:
:chatsupports SSE; this demo waits for the final response. For real UX, swapfetchfor anEventSource-style stream and append parts as they arrive. - Looker Embed SDK: for full filter-sync between dashboard and chat,
replace the plain iframe in
DashboardDetail.tsxwithLookerEmbedSDKand subscribe todashboard:filters:changedevents.
- Looker Conversational Analytics API docs
ca-demos-and-tools— official React + Node demoslooker-embedded-analytics-app-template- Google Codelab — "Build a chat React component with Looker Components"