A reference implementation demonstrating how to use the Looker SDK to connect to Conversational Analaytics in Looker.
This project provides a boilerplate for developers to build conversational data exploration interfaces. It showcases:
- Natural Language Querying: Direct interaction with data using natural language.
- Dynamic Data Visualization: Real-time rendering of charts (Vega-Lite) and tables.
- Agent Selection: Discovery and selection of data agents.
- AI-First UX: Streamlined transitions and visual feedback during CRUD operations.
- Home (app/page.tsx): The main orchestrator managing the primary layout and API stream parsing.
- LoadingOverlay: Global loading state during agent and session initialization.
- Chat Sidebar:
AgentSelector: Handles agent discovery, sorting, and switching.ConversationSelector: Manages the retrieval and selection of historical conversation sessions.ChatWindow: Manages the conversational flow, input handling, and real-time message streaming.ErrorMessage: Specialized display for API or networking failures with retry logic.
- Insight Workspace:
DataVisualization: The primary rendering engine that dynamically switches between tabular and graphical views.VegaChart: A specialized themed wrapper for Vega-Lite visualizations.
- LogDrawer: A collapsible overlay providing real-time developer logs and raw API stream inspection.
The application uses the Looker SDK to provide a responsive user experience:
- User submits a message via the UI.
- The application initiates the chat stream using
conversational_analytics_chatvia the Looker SDK. - The server-side route (Next.js API) manages the streaming connection to the Looker API.
- The response is streamed back to the client as a
ReadableStream. - The UI parses the stream, updating the
useChatStorestate in real-time. - After the stream concludes,
create_conversation_messageis called to persist the interaction history, ensuring context for future turns.
Managed via Zustand (src/store/useChatStore.ts):
- Messages: A unified history of user and system messages (including "thoughts" and "payloads").
- Agent Context: Stores discovered agents and the currently selected agent ID.
- Conversation State: Tracks the
conversationIdand session-specific metadata.
src/
βββ app/ # Next.js App Router (Pages & API Routes)
βββ components/ # UI Components (React)
βββ lib/ # Core Logic (Looker SDK interaction, Utilities)
βββ store/ # State Management (Zustand)
βββ test/ # Global Test Configuration
conductor/ # Spec-driven development framework artifacts
- Node.js 20+
- A Looker Instance (26.4+) with Conversational Analytics enabled.
npm installCreate a .env file in the root directory:
LOOKER_CLIENT_ID=your_client_id
LOOKER_CLIENT_SECRET=your_client_secret
NEXT_PUBLIC_LOOKER_BASE_URL=https://your-instance.looker.com
NEXT_PUBLIC_DEFAULT_AGENT_ID=your_agent_id
# Optional: Enable User OAuth Mode
NEXT_PUBLIC_LOOKER_AUTH_MODE=env
# NEXT_PUBLIC_LOOKER_OAUTH_CLIENT_ID=ca-looker-sdk-demoThe application supports two modes of authentication, managed via a global singleton SDK instance to ensure session stability:
- Service Account (Default): Uses
LOOKER_CLIENT_IDandLOOKER_CLIENT_SECRET. SetNEXT_PUBLIC_LOOKER_AUTH_MODE=env. All users interact with Looker via this single service account. - User OAuth: Enabled by setting
NEXT_PUBLIC_LOOKER_AUTH_MODE=oauth. Users must log in with their own Looker credentials via the OAuth 2.0 (PKCE) flow.
If you want to use User OAuth mode, you must register the application in your Looker instance.
Option 1: Using the provided script
./scripts/register-oauth.sh https://your-instance.looker.com YOUR_ADMIN_API_TOKENOption 2: Using the Looker API Explorer
- Navigate to your Looker instance's API Explorer.
- Use the
register_oauth_client_appmethod (API 4.0). - Set
client_guidtoca-looker-sdk-demo. - Set
redirect_uritohttp://localhost:3000/oauth/callback. - Set
display_nametoCA in Looker SDK Demo. - Set
enabledtotrue.
IMPORTANT: In both cases, you must also add your application's origin (e.g., http://localhost:3000) to the Embedded Domain Allowlist in Admin > Platform > Embed.
npm run devThe project uses Vitest for unit and component testing.
npm testnpm run test:watchThis application communicates via the Looker SDK to the Looker API. It does not use the GDA (geminidataanalytics.googleapis.com) API directly. This ensures that all data access adheres to Looker's centralized governance and security model.
This implementation uses the officially released Looker SDK (26.4+) which includes built-in methods for Conversational Analytics capabilities.
A key architectural difference between the Looker Conversational Analytics API and the Gemini Data Analytics API is the management of conversation state. While both APIs use chat with a conversation reference, the difference is:
- GDA API: Manages conversation state and history context automatically within the API backend.
- Looker CA API: Requires the developer to persist the message history after each full interaction turn (User Message + System Responses).
In this demo, the persistLookerMessages() function (found in src/lib/looker-conversation.ts) is called at the end of every successful chat stream. This function sends the complete interaction payload back to the Looker /conversations/{id}/messages endpoint. Failing to do this will result in the data agent "forgetting" the preceding conversation context in the next turn.
The project includes a Dockerfile for containerization. It builds the Next.js app.
The steps below will deploy the app the Cloud Run using Identity Aware Proxy (IAP) to require authentication.
For a streamlined deployment that automatically includes all environment variables from your .env file, you can use the provided scripts/deploy.sh script.
- Ensure you have a
.envfile populated with your configuration. - Run the script:
This script parses your local
chmod +x scripts/deploy.sh ./scripts/deploy.sh
.envfile and deploys the application to Cloud Run with all environment variables set.
-
Enable IAP on the Service
gcloud beta run services update looker-ca-reference-demo \ --iap \ --region us-central1
-
Configure the "Gatekeeper" (IAM)
Make sure to replace 'your-domain.com' below:
PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format="value(projectNumber)") gcloud projects add-iam-policy-binding $(gcloud config get-value project) \ --member="serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-iap.iam.gserviceaccount.com" \ --role="roles/run.invoker" gcloud beta iap web add-iam-policy-binding \ --resource-type=cloud-run \ --service=looker-ca-reference-demo \ --region=us-central1 \ --member='domain:your-domain.com' \ --role='roles/iap.httpsResourceAccessor'
