Code Review Patterns for This Stack
- Identify the three highest-risk areas specific to a Next.js/Stripe/Supabase stack: webhook handling, auth/ownership, and input validation
- Write a webhook-review prompt that checks signature verification on the raw body and resilience to out-of-order or duplicate delivery
- Write an auth-review prompt that distinguishes 401 (not signed in) from 403 (signed in, wrong owner) at the query level, not just the route level
- Write a validation-review prompt that checks for real bounds (length, range, enum) rather than just type checks
- Combine stack-specific risk categories into a single structured review prompt using the role/context/categories/severity skeleton from Track 4
- Explain why a generic code review prompt misses stack-specific failure modes that a tuned prompt catches
Generic Review Prompts Miss Stack-Specific Risk
The structured review patterns from Track 4, Lesson 6 -- naming a role, constraining categories, requiring severity ratings -- still apply here and are worth reviewing if you have not seen them. What this lesson adds is specific to the actual shape of risk in a Next.js + Stripe + Supabase app: the dangerous mistakes in this stack are not generic "off by one" bugs, they are framework- and integration-specific footguns that a generic "review this code" prompt will not know to look for. A webhook route, an auth check, and a validated input each fail in particular, recognizable ways in this stack -- so the review prompt should say so explicitly.
Risk Area 1: Webhook Handling
app/api/billing/webhook/route.ts is the highest-stakes file in this app -- it is the sole source of truth for who gets billed and who gets product access. A stack-aware review prompt for this file should ask specifically:
Review this Stripe webhook handler for: (1) does it verify the signature using the raw request body, not a parsed JSON body, (2) does it return early with a 4xx if the signature or secret is missing rather than silently proceeding, (3) for events that mutate billing state, is there a guard against out-of-order or duplicate delivery -- Stripe does not guarantee delivery order or exactly-once delivery, (4) do unhandled event types return success rather than an error that triggers a Stripe retry storm. Flag anything that assumes events arrive once, in order, or only of the types you explicitly handle.
That prompt is built directly from real decisions in this file: request.text() instead of request.json() because signature verification needs the raw bytes; an early 400 when stripe-signature or STRIPE_WEBHOOK_SECRET is missing; a default case that no-ops instead of erroring; and the delegation to reconcileSubscriptionUpdate specifically so a stale customer.subscription.deleted for an old subscription id cannot downgrade a business that has since resubscribed under a new id. A generic review prompt would never think to ask about delivery order, because that is not a generic programming concern -- it is a documented property of how Stripe's webhook system actually behaves.
Risk Area 2: Auth and Ownership Checks
Lesson 22 does a full audit of this app's auth boundaries in depth; for a per-diff code review prompt, the compressed version is enough to catch most regressions before they ship:
For any new or changed API route or Server Action in this app: does it call getCurrentUser() (never getSession() -- it is not safe to trust server-side) before touching the database, does it return 401 if there is no user, and if it operates on a specific resource by id, does the query either scope to the current user's business or explicitly verify ownership before any read or write? Flag any route that checks "is someone logged in" without also checking "does this person own the thing they are asking for."
The respond-to-review route is the real example this prompt is modeled on -- it checks getCurrentUser() first, then independently joins reviews to businesses and compares row.businessOwnerId !== user.id before allowing the write. A review prompt that only asks "is this authenticated" would pass code that lets any signed-in user respond to any business's reviews, because authentication and ownership are two different questions with two different status codes.
Risk Area 3: Input Validation at the Boundary
Every route in this app that accepts a request body validates it with a Zod schema via safeParse before any database call -- see app/api/reviews/ingest/route.ts and app/api/reviews/[id]/respond/route.ts. A stack-specific review prompt for this layer:
For any route accepting a request body: is request.json() wrapped in try/catch separately from schema validation (malformed JSON and a schema mismatch are different failures), does the Zod schema bound every field -- not just check its type but its length, range, or enum membership -- and does the route use safeParse rather than parse so a bad request returns a clean 400 instead of an uncaught exception. If a field also has a client-side constraint like maxLength, confirm the same bound is enforced server-side too, since a client constraint stops nothing on a direct API call.
The phrase "not just check its type but its length, range, or enum membership" is the part a generic prompt skips -- z.number() alone would accept a rating of 9000; the real schema in this app is z.number().int().min(1).max(5).
Putting It in One Prompt
For a real pull request against this codebase, the three risk areas above combine into a single structured prompt rather than three separate review passes:
You are reviewing a diff against a Next.js 16 / Supabase / Drizzle / Stripe / Resend app. Apply extra scrutiny in three areas if the diff touches them: webhook handlers (signature verification on the raw body, idempotency/ordering assumptions, safe handling of unrecognized event types), auth and ownership (getUser not getSession, 401 for "not signed in" vs 403 for "signed in but does not own this," ownership checked at the query level not just the route level), and input validation (Zod schemas with real bounds, not just types, validated with safeParse before any database write). For each issue found, cite the exact line, explain the specific failure mode in this stack -- not a generic description -- and rate severity Critical/High/Medium/Low.
This is the same structured-review skeleton from Track 4 Lesson 6 -- role, context, numbered categories, required severity -- with the categories swapped out for the three things that have actually caused real bugs or required real fixes in this specific app, including the webhook ordering bug documented in this repo's own debugging history. A code review prompt is only as good as how specific its categories are to the system it is reviewing; "check for security issues" and "check that webhook events are handled idempotently with ownership verified at the query level" will not produce the same review.
With a test suite and a review process in place, the dashboard is ready to go somewhere real users can reach it. The next lesson covers the actual mechanics of deploying this app to Vercel.
- A webhook review must ask whether the signature is verified against the raw request body and whether the handler tolerates out-of-order or duplicate event delivery -- Stripe guarantees neither ordering nor exactly-once delivery
- Auth review needs two separate checks: 401 for no valid session (getUser, never getSession, server-side) and 403 for a valid session that does not own the resource -- conflating them is the most common authorization bug
- Validation review should check that a Zod schema bounds length, range, or enum membership, not just type -- z.number() alone accepts any number, z.number().int().min(1).max(5) does not
- A client-side constraint like a maxLength attribute enforces nothing on a direct API call -- the server-side schema is the only real enforcement
- Combine stack-specific risk categories into the same role/context/categories/severity prompt skeleton from general code review patterns -- specificity in the categories is what changes the output quality
- The most useful review prompts cite the actual failure mode for this stack (e.g. webhook delivery ordering) rather than a generic security checklist item