Chat with your Google Analytics data using natural language. Connect your GA4 account and ask questions like "What are my top traffic sources?" or "How many users visited this week?"
- Local:
npm run dev(Next.js dev server). - Production (e.g. Code Capsules): Run Command =
npm install && npm run build && node server.js. No separate “build” step on the host;server.jsis a small Node server that serves the built Next app and listens onPORT/0.0.0.0.
- Frontend: Next.js 16 + React 19 + Tailwind CSS v4
- Auth: NextAuth v5 with Google OAuth (requesting
analytics.readonlyscope) - LLM: Claude (Anthropic) with tool calling — the model decides which GA4 queries to run
- Analytics: Google Analytics Data API v1beta via the
googleapisSDK
Auth methods: Google OAuth + Email/Password (Credentials). Both are configured in src/auth.ts via NextAuth v5.
| Method | Flow |
|---|---|
User clicks "Sign up with Google" → signIn("google", { callbackUrl: "/pricing" }) → NextAuth redirects to Google → callback at /api/auth/callback/google → user created via PrismaAdapter → redirect to /pricing (NextAuth newUser page). |
|
| Email/Password | User submits name, email, password (min 8 chars) → POST /api/auth/register creates User with passwordHash (bcrypt, 12 rounds) → then signIn("credentials", { email, password }) with redirect: false → on success, client redirects to /pricing. |
| Method | Flow |
|---|---|
User clicks "Log in with Google" → signIn("google", { callbackUrl }) → NextAuth handles OAuth → callback creates/finds user → redirect to callbackUrl (default /) or /pricing for new users. |
|
| Email/Password | User submits email + password → signIn("credentials", ...) → Credentials provider in auth.ts looks up User by email, compares passwordHash with bcrypt → on success, client redirects to callbackUrl. |
src/auth.ts— NextAuth config:pages.signIn: "/login"— unauthenticated users go herepages.newUser: "/pricing"— new Google users land herepages.error: "/auth-error"— OAuth/config errorssession: { strategy: "jwt" }— JWT sessions (OAuth tokens stored in JWT)
/api/auth/register— CreatesUserwithpasswordHash; does not create anAccount. Duplicate email returns 409.src/middleware.ts— Protects/accountand/connect-analytics; redirects to/loginwithcallbackUrl=/connect-analyticswhen needed.
Critical requirements:
DATABASE_URL— required for PrismaAdapter and Credentials lookupUser.passwordHash— must exist in schema (nullable); OAuth-only users havenull- Google redirect URIs must include both local and prod (e.g.
http://localhost:3001/api/auth/callback/google)
- User signs in with Google and grants read-only access to their Analytics.
- User selects a GA4 property from the dropdown.
- User asks a question in the chat interface.
- The backend sends the question to Claude along with GA4 tool definitions (
run_report,run_realtime_report,get_metadata). - Claude decides which tools to call and with what parameters.
- The backend executes the GA4 API calls using the user's OAuth token and returns results to Claude.
- Claude interprets the data and responds with insights in plain English.
- Create a project at console.cloud.google.com
- Enable these APIs:
- Google Analytics Data API
- Google Analytics Admin API
- Go to APIs & Services > Credentials and create an OAuth 2.0 Client ID (Web application)
- Add both redirect URIs (local and prod) so OAuth works everywhere:
- Local:
http://localhost:3001/api/auth/callback/google - Prod: e.g.
https://your-app.usemeaning.io/api/auth/callback/google
- Local:
- (Optional) Under Authorized JavaScript origins, add
http://localhost:3001for local. - Copy the Client ID and Client Secret.
cp .env.example .envFill in your .env:
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
ANTHROPIC_API_KEY=your_anthropic_api_key
AUTH_SECRET=<run: openssl rand -base64 32>
NEXTAUTH_URL=http://localhost:3001
# Required for OAuth and account creation (NextAuth + Prisma).
# Local: use a Supabase project (free tier) or local Postgres — see "Local development with Supabase" below.
DATABASE_URL="postgresql://..."
After moving prod to Supabase, local needs a Postgres DB and the right Google redirect URI.
-
Database for local (pick one):
- Option A — Same Supabase project as prod: Use your prod
DATABASE_URLin.env. Easiest; local and prod share the same DB (fine for solo dev; be careful with data). - Option B — Separate Supabase project for dev: Create a second Supabase project (free tier), get its connection string from Project Settings → Database → Connection string (URI), put it in
.envasDATABASE_URL, then run:so the schema is applied to the dev DB.npx prisma migrate deploy
- Option A — Same Supabase project as prod: Use your prod
-
.envfor local: Ensure:NEXTAUTH_URL=http://localhost:3001DATABASE_URL= one of the options aboveAUTH_SECRET,GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRETset (same Google OAuth client as prod is fine).
-
Google OAuth: In Google Cloud Console → APIs & Services → Credentials → your OAuth 2.0 client:
- Authorized redirect URIs: add
http://localhost:3001/api/auth/callback/google(in addition to your prod callback URL). - Save. Without this, Google will return
redirect_uri_mismatchwhen signing in locally.
- Authorized redirect URIs: add
After that, npm run dev and sign in with Google at http://localhost:3001; the callback will hit localhost and NextAuth will use your local DATABASE_URL to create/find the user.
cp .env.example .env
# Edit .env and add your values (see above). For a quick local test, at minimum set AUTH_SECRET to any non-empty string.
npm install
npm run devOpen http://localhost:3001. Sign in with Google (needs real GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET and the local redirect URI in Google Console), select a GA4 property, and start chatting. Chat requires a valid ANTHROPIC_API_KEY. OAuth requires a valid DATABASE_URL (see "Local development with Supabase" above).
This repo is set up for Code Capsules (no separate build phase there, so install + build + start happen in one Run Command).
| Where | How you run it |
|---|---|
| Local | npm run dev (Next.js dev server). |
| Code Capsules | Run Command: npm install && npm run build && node server.js. The custom server.js listens on PORT and 0.0.0.0 so the capsule can reach it. |
-
Create a Backend Capsule and connect this GitHub repo.
-
Capsule Parameters (Config tab → Edit Capsule Parameters):
- Code Capsules has no separate build step, so use the Run Command to install, build, and start in one go. Set Run Command to:
npm install && npm run build && node server.js
- Network Port:
3000(default).
- Code Capsules has no separate build step, so use the Run Command to install, build, and start in one go. Set Run Command to:
-
Environment variables (Config tab): add the same as in Setup, with production values:
GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETANTHROPIC_API_KEYAUTH_SECRET(e.g.openssl rand -base64 32)NEXTAUTH_URL= your capsule URL, e.g.https://<your-capsule>.codecapsules.space
-
Google Cloud Console → your OAuth 2.0 client → add authorized redirect URI:
https://<your-capsule>.codecapsules.space/api/auth/callback/google -
Redeploy. The run command installs deps, builds the app, then starts the custom server so the app listens on Code Capsules’ port and is reachable.
If you see "Service Unavailable" (503):
- Open the capsule Logs tab (runtime logs, not build logs) and check for errors when the app starts or when you open the site. Common causes:
- Missing env vars:
AUTH_SECRETandNEXTAUTH_URLmust be set; without them the app can crash on requests. - Wrong port: In Capsule Parameters, Network Port should be
3000so it matches what the app uses.
- Missing env vars:
- You can ping the health endpoint to confirm the process is up:
https://<your-capsule>.codecapsules.space/api/health(should return{"ok":true}).
Why won’t it deploy?
- Run Command not set — Code Capsules doesn’t have a separate “build” step. If Run Command is blank, it may only run
npm start, so the app never runsnpm run buildand there’s no.nextfolder → 502. Fix: Set Run Command tonpm install && npm run build && node server.js. - Wrong port — Capsule Parameters → Network Port must be
3000so it matches whatserver.jsuses (process.env.PORTor 3000). - Missing env vars —
AUTH_SECRETandNEXTAUTH_URLmust be set or the app can crash. Add all variables from the table above. - Check runtime logs — In the capsule’s Logs tab (not the build log), look for errors when the app starts or when you open the site. You should see
> Ready on http://0.0.0.0:3000; if not, the log will show the failure.
See CODECAPSULES.md for a step-by-step deployment checklist.
| Tool | Description |
|---|---|
run_report |
Query historical data — metrics, dimensions, date ranges, sorting |
run_realtime_report |
Real-time data from the last 30 minutes |
get_metadata |
Discover available metrics and dimensions for the property |
- "How many active users did I have this month?"
- "What are my top 10 pages by views?"
- "Where is my traffic coming from?"
- "Compare this week's sessions to last week"
- "What devices do my users use?"
- "Who is on my site right now?"
- "What's my bounce rate trend over the last 30 days?"