Skip to content

[CI] (3393d57) next-js/15-pages-router-saas#887

Closed
wizard-ci-bot[bot] wants to merge 1 commit intomainfrom
wizard-ci-3393d57-next-js-15-pages-router-saas
Closed

[CI] (3393d57) next-js/15-pages-router-saas#887
wizard-ci-bot[bot] wants to merge 1 commit intomainfrom
wizard-ci-3393d57-next-js-15-pages-router-saas

Conversation

@wizard-ci-bot
Copy link

@wizard-ci-bot wizard-ci-bot bot commented Mar 17, 2026

Automated wizard CI run

Source: wizard-pr
Trigger ID: 3393d57
App: next-js/15-pages-router-saas
App directory: apps/next-js/15-pages-router-saas
Workbench branch: wizard-ci-3393d57-next-js-15-pages-router-saas
Wizard branch: release-please--branches--main--components--wizard
Context Mill branch: main
PostHog (MCP) branch: master
Timestamp: 2026-03-17T00:20:47.893Z
Duration: 385.5s

@wizard-ci-bot
Copy link
Author

wizard-ci-bot bot commented Mar 17, 2026

Now I have all the context I need. Let me produce the evaluation report.


PR Evaluation Report

Summary

This PR integrates PostHog into a Next.js 15 Pages Router SaaS application with both client-side (posthog-js) and server-side (posthog-node) SDKs. It adds initialization via instrumentation-client.ts, a reverse proxy via Next.js rewrites, server-side tracking across auth/billing/team API routes, client-side event capture and user identification, and error tracking via capture_exceptions.

Files changed Lines added Lines removed
16 +199 -1

Confidence score: 4/5 👍

  • PII in server-side capture events: Multiple posthog.capture() calls on the server include email directly in event properties (sign-in, sign-up, team invite). PII should only appear in person properties via identify(), not in capture() event properties. [CRITICAL]
  • Client-side identify uses email as distinct_id: In login.tsx, posthog.identify(data.email, { email: data.email }) uses the raw email address as the distinct_id. This conflicts with server-side identify which uses String(foundUser.id). This mismatch means client and server events will be attributed to different persons, causing fragmented data. The client should use the database user ID, not the email. [CRITICAL]
  • Missing await posthog.shutdown(): The server-side PostHog client is a reusable singleton, yet none of the API routes call await posthog.shutdown() after capturing events. For Next.js API routes (which are short-lived), the docs explicitly recommend flushAt: 1 and flushInterval: 0 (which are set), but shutdown() is the recommended practice. The singleton pattern with those flush settings partially mitigates this, but events may still be lost on cold starts or edge deploys. [MEDIUM]
  • Environment variables not documented: The .env.example file was not updated with NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN or NEXT_PUBLIC_POSTHOG_HOST. Developers cloning the repo won't know these variables are needed. [MEDIUM]

File changes

Filename Score Description
instrumentation-client.ts 4/5 Properly initializes posthog-js with env var for token, reverse proxy host, defaults, error tracking, and debug mode
lib/posthog-server.ts 4/5 Singleton pattern for posthog-node with correct flushAt: 1 and flushInterval: 0 for short-lived contexts
next.config.ts 5/5 Correct reverse proxy rewrites for PostHog with skipTrailingSlashRedirect
package.json 5/5 Both posthog-js and posthog-node added as dependencies
components/login.tsx 2/5 Uses email as distinct_id in identify(), creating a client-server identity mismatch
components/header.tsx 5/5 Captures sign-out event and correctly calls posthog.reset()
pages/api/auth/sign-in.ts 3/5 Good server-side identify and capture, but email is PII in capture properties
pages/api/auth/sign-up.ts 3/5 Good server-side identify and capture, but email is PII in capture properties
pages/api/stripe/checkout.ts 5/5 Clean checkout_completed event with relevant billing properties
pages/api/stripe/webhook.ts 4/5 Tracks subscription changes; uses Stripe customer ID as distinct_id (not ideal but acceptable for webhook context)
pages/api/team/invite.ts 3/5 Tracks team invite but includes invited_email (PII) in event properties
pages/api/team/remove-member.ts 5/5 Clean event with no PII
pages/dashboard/general.tsx 3/5 Captures account_updated but sends email in event properties
pages/pricing.tsx 5/5 Good checkout_initiated event with relevant pricing properties
.gitignore 5/5 Correctly ignores .env.local
posthog-setup-report.md N/A Documentation file, not evaluated

App sanity check ⚠️

Criteria Result Description
App builds and runs Yes No syntax or type errors detected; all imports resolve
Preserves existing env vars & configs Yes Existing next.config.ts settings preserved; only PostHog additions
No syntax or type errors Yes All TypeScript code appears valid
Correct imports/exports Yes posthog-js imported client-side, posthog-node server-side — correct separation
Minimal, focused changes Yes All changes are PostHog-related
Pre-existing issues None No pre-existing issues observed

Issues

  • Environment variables not documented in .env.example: NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN and NEXT_PUBLIC_POSTHOG_HOST are not added to .env.example. Developers will not know these variables are required. Add them to .env.example. [MEDIUM]

Other completed criteria

  • Build configuration is valid — package.json has both PostHog packages listed
  • All imports correctly reference the right packages for their execution context
  • No unrelated changes or scope creep

PostHog implementation ⚠️

Criteria Result Description
PostHog SDKs installed Yes posthog-js@^1.360.2 and posthog-node@^5.28.2 added to package.json
PostHog client initialized Yes Client initialized in instrumentation-client.ts with env token, reverse proxy host, defaults, and capture_exceptions; server singleton in lib/posthog-server.ts
capture() Yes Multiple meaningful capture calls across client (sign-out, checkout_initiated, account_updated) and server (sign-in, sign-up, checkout_completed, subscription_updated, team events)
identify() No Client-side identify() uses raw email as distinct_id, while server-side uses String(user.id). This mismatch prevents proper identity merging and fragments user data.
Error tracking Yes capture_exceptions: true enabled in instrumentation-client.ts init config
Reverse proxy Yes Next.js rewrites properly configured in next.config.ts pointing /ingest/* to us.i.posthog.com and /ingest/static/* to us-assets.i.posthog.com

Issues

  • Client-server distinct_id mismatch: login.tsx calls posthog.identify(data.email, ...) using raw email, while server routes use String(foundUser.id). PostHog will treat these as separate persons. The client-side identify should use the user's database ID (returned from the API response) as the distinct_id. [CRITICAL]
  • API key env var name inconsistency: Client init uses NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN while server uses both NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN (for the token) and NEXT_PUBLIC_POSTHOG_HOST (for host). The client api_host is hardcoded to '/ingest' rather than using the env var — this is actually fine for the reverse proxy pattern, but the server client's host references NEXT_PUBLIC_POSTHOG_HOST which is never defined in .env.example. [LOW]

Other completed criteria

  • API key loaded from environment variable, not hardcoded
  • Host correctly configured — client uses reverse proxy /ingest, server uses env var for PostHog host
  • posthog.reset() called on sign-out (best practice)
  • Server-side singleton pattern with flushAt: 1 and flushInterval: 0 follows Next.js docs

PostHog insights and events ⚠️

Filename PostHog events Description
components/header.tsx user_signed_out Client-side sign-out tracking with proper posthog.reset()
components/login.tsx identify Client-side user identification on sign-in/sign-up success
pages/pricing.tsx checkout_initiated Captures plan selection with name, price_id, price, interval
pages/dashboard/general.tsx account_updated Captures account settings save with name and email
pages/api/auth/sign-in.ts user_signed_in, identify Server-side sign-in tracking with person properties
pages/api/auth/sign-up.ts user_signed_up, identify Server-side sign-up tracking with role, team, invite info
pages/api/stripe/checkout.ts checkout_completed Server-side checkout completion with plan/subscription details
pages/api/stripe/webhook.ts subscription_updated Webhook-triggered subscription status changes
pages/api/team/invite.ts team_member_invited Server-side team invitation tracking
pages/api/team/remove-member.ts team_member_removed Server-side team member removal tracking

Issues

  • PII in event properties: Email addresses appear in capture() event properties in sign-in.ts, sign-up.ts, invite.ts, and general.tsx. Email is PII and should only be set via identify() person properties, not sent with every event capture. Remove email from capture properties. [CRITICAL]
  • Email in account_updated event: general.tsx sends { name: data.name, email: data.email } in account_updated capture — both name and email are PII. [MEDIUM]
  • Webhook distinct_id is Stripe customer ID: webhook.ts uses subscription.customer (Stripe customer ID like cus_xxx) as distinctId. This won't match the app's user ID (String(user.id)), so these events won't be linked to the same person. [MEDIUM]

Other completed criteria

  • Events represent real user actions (sign-up, sign-in, checkout, team management)
  • Events enable meaningful product insights — can build sign-up funnel, checkout conversion, team growth trends
  • Events include enriched properties (plan names, subscription IDs, roles, etc.)
  • Event naming is descriptive and uses consistent snake_case convention

Reviewed by wizard workbench PR evaluator

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