Skip to content

[CI] (d819646) next-js/15-pages-router-saas#1496

Closed
wizard-ci-bot[bot] wants to merge 1 commit into
mainfrom
wizard-ci-d819646-next-js-15-pages-router-saas
Closed

[CI] (d819646) next-js/15-pages-router-saas#1496
wizard-ci-bot[bot] wants to merge 1 commit into
mainfrom
wizard-ci-d819646-next-js-15-pages-router-saas

Conversation

@wizard-ci-bot
Copy link
Copy Markdown

@wizard-ci-bot wizard-ci-bot Bot commented May 14, 2026

Automated wizard CI run

Source: manual
Trigger ID: d819646
App: next-js/15-pages-router-saas
App directory: apps/next-js/15-pages-router-saas
Workbench branch: wizard-ci-d819646-next-js-15-pages-router-saas
Wizard branch: main
Context Mill branch: main
PostHog (MCP) branch: master
Timestamp: 2026-05-14T16:43:33.784Z
Duration: 730.8s

@wizard-ci-bot
Copy link
Copy Markdown
Author

wizard-ci-bot Bot commented May 14, 2026

The .env.example was not updated with the PostHog env vars, and the .env.local contains what appears to be a real (though likely test) API key. Now I have everything I need.


PR Evaluation Report

Summary

This PR integrates PostHog into a Next.js 15 Pages Router SaaS application, adding both client-side (posthog-js) and server-side (posthog-node) tracking. It initializes PostHog via instrumentation-client.ts, configures a reverse proxy through Next.js rewrites, and instruments 11 custom events across auth flows, Stripe checkout/webhooks, and team management.

Files changed Lines added Lines removed
15 +174 -1

Confidence score: 4/5 👍

  • PII in event properties: Multiple capture() calls include email in event properties (e.g., user_signed_in, user_signed_up, account_updated, team_member_invited). Emails are PII and belong in identify() person properties, not in capture event properties. [CRITICAL]
  • Email used as distinct_id: posthog.identify(data.email, ...) uses raw email as the distinct_id on both client and server. This is fragile (emails change) and leaks PII into the distinct_id field. Should use a stable database user ID instead. [CRITICAL]
  • Stripe webhook uses Stripe customer ID as distinct_id: The webhook handler uses customerId (Stripe cus_xxx string) as distinctId, which won't match the email-based distinct_id used elsewhere, causing fragmented user data in PostHog. [CRITICAL]
  • Missing await posthog.shutdown(): None of the server-side API routes call await posthog.shutdown() after capturing events. The Next.js Pages Router docs explicitly require this to ensure batched events flush before the response ends. [MEDIUM]
  • .env.example not updated: The PostHog env vars (NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOST) are not added to .env.example, only to .env.local. [MEDIUM]

File changes

Filename Score Description
instrumentation-client.ts 5/5 Correctly initializes posthog-js with env var, reverse proxy host, defaults, exception capture, and debug mode
lib/posthog-server.ts 4/5 Singleton posthog-node client with correct flushAt/flushInterval settings; no shutdown pattern
next.config.ts 5/5 Proper reverse proxy rewrites for /ingest with static/array asset routing and skipTrailingSlashRedirect
package.json 5/5 Both posthog-js and posthog-node added with appropriate versions
components/login.tsx 2/5 Uses email as distinct_id, sends email in capture event properties (PII), and captureException on error
pages/api/auth/sign-in.ts 2/5 Server-side identify/capture with email as distinct_id, email in event props, no shutdown()
pages/api/auth/sign-up.ts 2/5 Same issues: email as distinct_id, PII in events, no shutdown()
pages/api/auth/sign-out.ts 3/5 Captures sign-out event with email distinct_id, no shutdown()
pages/api/stripe/create-checkout.ts 3/5 Captures checkout event with good properties, uses email as distinct_id, no shutdown()
pages/api/stripe/webhook.ts 2/5 Uses Stripe customer ID as distinct_id (mismatched with rest of app), no shutdown()
pages/dashboard/general.tsx 3/5 Captures account_updated with email in props (PII), good exception handling
pages/dashboard/index.tsx 3/5 Good events for team management, invited_email is PII in event props
pages/pricing.tsx 4/5 Good checkout_started event with meaningful properties, captureException on error
.gitignore 5/5 Correctly ignores .env.local
posthog-setup-report.md 4/5 Comprehensive setup report

App sanity check ⚠️

Criteria Result Description
App builds and runs Yes No syntax errors, valid TypeScript, all imports resolve
Preserves existing env vars & configs Yes Existing next.config.ts extended, no existing code removed
No syntax or type errors Yes All changes are syntactically valid
Correct imports/exports Yes posthog-js imported client-side, posthog-node server-side, correct package names
Minimal, focused changes Yes All changes are PostHog-related
Pre-existing issues None Base app appears well-structured

Issues

  • .env.example not updated with PostHog vars: The .env.example file does not include NEXT_PUBLIC_POSTHOG_KEY or NEXT_PUBLIC_POSTHOG_HOST. Other developers cloning the repo won't know these are needed. [MEDIUM]
  • Missing await posthog.shutdown() in API routes: Server-side API routes (sign-in, sign-up, sign-out, create-checkout, webhook) don't call await posthog.shutdown(). The Next.js Pages Router docs require this because server-side functions can be short-lived and events may be lost. [MEDIUM]

Other completed criteria

  • App builds without syntax or type errors
  • Existing app code and configs preserved correctly
  • Changes are minimal and focused on PostHog integration
  • All imports resolve correctly (posthog-js client-side, posthog-node server-side)
  • Build configuration (package.json, next.config.ts) is valid

PostHog implementation ⚠️

Criteria Result Description
PostHog SDKs installed Yes posthog-js@^1.373.4 and posthog-node@^5.34.1 added to package.json
PostHog client initialized Yes instrumentation-client.ts uses posthog.init() with env var, reverse proxy, defaults, and capture_exceptions
capture() Yes 11 meaningful custom events across auth, checkout, subscription, and team management flows
identify() No Uses raw email as distinct_id instead of stable database user ID; webhook uses Stripe customer ID causing fragmented data
Error tracking Yes captureException() in all client-side catch blocks + capture_exceptions: true in init for autocapture
Reverse proxy Yes Next.js rewrites correctly route /ingest/static/* and /ingest/array/* to us-assets.i.posthog.com, /ingest/* to us.i.posthog.com, with skipTrailingSlashRedirect

Issues

  • Email used as distinct_id: posthog.identify(data.email, ...) on client and posthog.identify({ distinctId: foundUser.email, ... }) on server use raw email as the distinct_id. Emails are PII, can change, and are not stable identifiers. The app has database user IDs (foundUser.id, createdUser.id) that should be used instead. [CRITICAL]
  • Fragmented identity in Stripe webhook: The webhook handler uses subscription.customer (Stripe's cus_xxx ID) as the distinctId. This won't match the email-based distinct_id used in all other routes, creating separate person profiles in PostHog for the same user. [CRITICAL]
  • No posthog.reset() on sign-out client-side: The client-side sign-out flow doesn't call posthog.reset(). The docs explicitly recommend calling reset on logout to unlink future events from the current user. [MEDIUM]

Other completed criteria

  • PostHog SDKs correctly installed in package.json
  • Client initialized via instrumentation-client.ts following Next.js 15.3+ pattern
  • API key loaded from NEXT_PUBLIC_POSTHOG_KEY environment variable
  • Reverse proxy correctly configured with both static/array asset routes and main ingest route
  • Server-side singleton pattern with flushAt: 1 and flushInterval: 0 for immediate flushing
  • Exception autocapture enabled via capture_exceptions: true

PostHog insights and events ⚠️

Filename PostHog events Description
components/login.tsx user_signed_in, user_signed_up, captureException Client-side auth events with identify on login/signup
pages/api/auth/sign-in.ts user_signed_in Server-side sign-in tracking with identify
pages/api/auth/sign-up.ts user_signed_up Server-side sign-up tracking with identify
pages/api/auth/sign-out.ts user_signed_out Server-side sign-out event
pages/pricing.tsx checkout_started, captureException Client-side checkout initiation with plan details
pages/api/stripe/create-checkout.ts checkout_session_created Server-side checkout session creation
pages/api/stripe/webhook.ts subscription_updated, subscription_cancelled Stripe webhook subscription lifecycle events
pages/dashboard/index.tsx customer_portal_opened, team_member_removed, team_member_invited, captureException Dashboard team management and billing events
pages/dashboard/general.tsx account_updated, captureException Account settings update event

Issues

  • PII (email) in capture event properties: user_signed_in, user_signed_up, account_updated, and team_member_invited events include email addresses in event properties. Emails are PII and should only be set via identify() person properties, never in capture() event properties. This applies to both client-side ({ email: data.email }) and server-side ({ email: foundUser.email }) captures. [CRITICAL]

Other completed criteria

  • Events represent real user actions mapping to actual SaaS product flows (auth, checkout, subscription, team management)
  • Events enable product insights: sign-up → checkout → subscription funnel, churn tracking via subscription_cancelled, team growth via invites
  • Events include relevant contextual properties (price_id, plan_name, subscription_id, member_id, etc.)
  • Event names are descriptive, consistent snake_case, and clearly describe the action

Reviewed by wizard workbench PR evaluator

@wizard-ci-bot wizard-ci-bot Bot added the CI/CD label May 14, 2026
@wizard-ci-bot wizard-ci-bot Bot closed this May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants