feat: upgrade free-tier stack from Supabase to Neon + Better Auth + Drizzle#7
Conversation
…rizzle Replace Supabase (database + auth + RLS) with a stronger free-tier combination: - Neon for PostgreSQL (auto-suspend with instant resume, free branching) - Better Auth for authentication (open-source, DB-backed sessions) - Drizzle ORM for type-safe schema management and migrations - Vercel Blob for file storage Updates 13 files across scaffold templates, docs, security scripts, and CLI allow-lists. Security model shifts from database-level RLS to application-level middleware and server guards. https://claude.ai/code/session_01Vt7EoDLuUcFLqAwAD155q2
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughThis pull request migrates repository guidance and templates from a Supabase-centered stack to a Neon + Better Auth + Drizzle ORM + Vercel Blob stack. It updates multiple docs (AGENTS.md, CLAUDE.md, docs/*), scaffold templates (.env.example, CLAUDE.md.template, AGENTS.md.template), a Claude settings allowlist, and scripts (security-check.sh, init.sh). Environment variables, CLI/migration workflows, auth/session handling, storage guidance, and security checks are revised to use Neon connection strings, Better Auth secrets, Drizzle Kit/neonctl commands, and Vercel Blob tokens; database RLS guidance is replaced by application-level access control. Sequence Diagram(s)sequenceDiagram
participant Client
participant NextJS_Server as Next.js Middleware/API
participant BetterAuth as Better Auth
participant DB as Neon (via Drizzle)
participant Blob as Vercel Blob
Client->>NextJS_Server: request (SSR or API)
NextJS_Server->>BetterAuth: auth.api.getSession() / toNextJsHandler
BetterAuth-->>NextJS_Server: session token / user info
NextJS_Server->>DB: query via Drizzle (server uses DATABASE_URL)
DB-->>NextJS_Server: query results
NextJS_Server->>Blob: signed upload/download using BLOB_READ_WRITE_TOKEN (server-only)
NextJS_Server-->>Client: rendered response / API JSON (no server secrets exposed)
sequenceDiagram
actor Developer
participant DrizzleCLI as drizzle-kit
participant NeonCtl as neonctl / Neon
participant CI as CI / Vercel
participant DB as Neon Database
Developer->>DrizzleCLI: edit schema, pnpm drizzle-kit generate
DrizzleCLI->>DrizzleCLI: produce migration SQL
Developer->>NeonCtl: push using DATABASE_URL_UNPOOLED (drizzle-kit push / neonctl)
NeonCtl->>DB: apply migrations to branch database
Developer->>CI: push branch / create preview
CI->>NeonCtl: create/delete preview branch (optional)
CI->>DB: run migration push during deploy (using unpooled connection)
CI-->>Developer: deploy status / migration result
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
scaffold/.claude/settings.json (1)
43-49: Consider adding npm/yarn variants for drizzle-kit commands.The allow-list includes npm, pnpm, and yarn variants for other commands (lines 4-33), but
drizzle-kitcommands only havepnpmvariants. If a downstream project uses npm or yarn, these drizzle-kit commands won't be pre-authorized.♻️ Suggested additions for consistency
"Bash(pnpm drizzle-kit generate*)", "Bash(pnpm drizzle-kit push*)", "Bash(pnpm drizzle-kit migrate*)", "Bash(pnpm drizzle-kit studio*)", + "Bash(npm run drizzle-kit generate*)", + "Bash(npm run drizzle-kit push*)", + "Bash(npm run drizzle-kit migrate*)", + "Bash(npm run drizzle-kit studio*)", + "Bash(yarn drizzle-kit generate*)", + "Bash(yarn drizzle-kit push*)", + "Bash(yarn drizzle-kit migrate*)", + "Bash(yarn drizzle-kit studio*)", "Bash(neonctl branches *)",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scaffold/.claude/settings.json` around lines 43 - 49, The allow-list only includes pnpm variants for the drizzle-kit commands ("Bash(pnpm drizzle-kit generate*)", "Bash(pnpm drizzle-kit push*)", "Bash(pnpm drizzle-kit migrate*)", "Bash(pnpm drizzle-kit studio*)", plus neonctl entries) so add matching npm and yarn variants for each drizzle-kit entry (e.g., add "Bash(npm drizzle-kit generate*)", "Bash(yarn drizzle-kit generate*)", and the same for push*, migrate*, studio*) to the JSON array to match the existing npm/yarn coverage for other commands.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/AX_UPGRADE_REPORT.md`:
- Line 110: The follow-up suggestion uses the non-existent --dry-run flag for
pnpm drizzle-kit push; remove --dry-run and replace it with a valid validation
approach such as using the CLI in interactive/verbose mode (pnpm drizzle-kit
push --verbose --strict) to review SQL before confirming, or recommend the
programmatic API approach using drizzle-kit/api push*Schema(...), inspect
result.statementsToExecute and skip calling result.apply() to validate without
applying migrations; update the suggestion to show one of these valid options.
In `@scripts/init.sh`:
- Around line 1582-1584: The glob in the case that sets IS_CLIENT_FILE is
inconsistent—change the pattern matching for components to include the same
leading directory wildcard as the others so deeply nested paths are detected;
update the case pattern that currently reads the alternatives for "$file" (the
branch that sets IS_CLIENT_FILE=true) to use */components/* instead of
*components/* so all client paths (app, pages, components) are matched
consistently.
---
Nitpick comments:
In `@scaffold/.claude/settings.json`:
- Around line 43-49: The allow-list only includes pnpm variants for the
drizzle-kit commands ("Bash(pnpm drizzle-kit generate*)", "Bash(pnpm drizzle-kit
push*)", "Bash(pnpm drizzle-kit migrate*)", "Bash(pnpm drizzle-kit studio*)",
plus neonctl entries) so add matching npm and yarn variants for each drizzle-kit
entry (e.g., add "Bash(npm drizzle-kit generate*)", "Bash(yarn drizzle-kit
generate*)", and the same for push*, migrate*, studio*) to the JSON array to
match the existing npm/yarn coverage for other commands.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3d379fe7-e5fc-4832-85a9-3fbceeaff482
📒 Files selected for processing (14)
AGENTS.mdCLAUDE.mddocs/ARCHITECTURE.mddocs/AX_UPGRADE_REPORT.mddocs/DECISIONS.mddocs/ENV.mddocs/RUNBOOK.mddocs/TROUBLESHOOTING.mdscaffold/.claude/settings.jsonscaffold/.env.examplescaffold/AGENTS.md.templatescaffold/CLAUDE.md.templatescaffold/scripts/security-check.shscripts/init.sh
- Replace invalid `--dry-run` flag in drizzle-kit CI follow-up with valid approaches (--verbose --strict or programmatic API) - Fix inconsistent glob: `*components/*` → `*/components/*` in security-check.sh client-path detection - Add npx and yarn drizzle-kit variants to settings.json allow-list to match existing npm/pnpm/yarn coverage - Regenerate init.sh to propagate security-check and settings changes https://claude.ai/code/session_01Vt7EoDLuUcFLqAwAD155q2
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/AX_UPGRADE_REPORT.md`:
- Line 104: Update the absolute verification sentence "No remaining references
to Supabase in scaffold/ or docs/ files." to be less absolute and reflect intent
for active guidance only; replace it with phrasing such as "No remaining active
guidance referencing Supabase in scaffold/ or docs/ files." so
historical/migration mentions are allowed, and ensure the change is applied
where that exact sentence appears (the line containing the quoted text) in
AX_UPGRADE_REPORT.md.
In `@scaffold/scripts/security-check.sh`:
- Around line 122-123: The server-secret scan conditional in
scaffold/scripts/security-check.sh that checks if a line contains
"(DATABASE_URL|BETTER_AUTH_SECRET)" should also include DATABASE_URL_UNPOOLED;
update the grep/regex used in the if-condition (the conditional that sets
IS_CLIENT_FILE=false) to match
"(DATABASE_URL|DATABASE_URL_UNPOOLED|BETTER_AUTH_SECRET)" (and apply the same
change to the other identical occurrence later in the script around the second
conditional). This ensures lines containing DATABASE_URL_UNPOOLED are treated as
server-only secrets and not flagged as client files.
- Line 125: The case pattern `*/app/*|*/pages/*|*/components/*)` only matches
nested paths and misses root-level folders; update the case arm that sets
IS_CLIENT_FILE=true to include both root and nested patterns (e.g., add
`app/*|pages/*|components/*` alongside the existing `*/...` variants) and quote
each pattern containing `*` (e.g.,
`"app/*"|"*/app/*"|"pages/*"|"*/pages/*"|"components/*"|"*/components/*"`) so
the shell parses them correctly; keep the assignment to IS_CLIENT_FILE
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7e270a79-3451-4c55-8b5c-8de5d1e2e802
📒 Files selected for processing (4)
docs/AX_UPGRADE_REPORT.mdscaffold/.claude/settings.jsonscaffold/scripts/security-check.shscripts/init.sh
✅ Files skipped from review due to trivial changes (1)
- scripts/init.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- scaffold/.claude/settings.json
| if echo "$line" | grep -qE "(DATABASE_URL|BETTER_AUTH_SECRET)" 2>/dev/null; then | ||
| IS_CLIENT_FILE=false |
There was a problem hiding this comment.
Include DATABASE_URL_UNPOOLED in the server-secret scan.
Line 122 currently misses DATABASE_URL_UNPOOLED, which is also a server-only DB credential in this migration. That leaves a client-leak blind spot.
Suggested patch
- if echo "$line" | grep -qE "(DATABASE_URL|BETTER_AUTH_SECRET)" 2>/dev/null; then
+ if echo "$line" | grep -qE "(DATABASE_URL(_UNPOOLED)?|BETTER_AUTH_SECRET)" 2>/dev/null; then
@@
- *) warn "Server-only secret (DATABASE_URL or BETTER_AUTH_SECRET) in client-accessible path" "$file" "$LINE_NUM" ;;
+ *) warn "Server-only secret (DATABASE_URL, DATABASE_URL_UNPOOLED, or BETTER_AUTH_SECRET) in client-accessible path" "$file" "$LINE_NUM" ;;Also applies to: 135-135
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scaffold/scripts/security-check.sh` around lines 122 - 123, The server-secret
scan conditional in scaffold/scripts/security-check.sh that checks if a line
contains "(DATABASE_URL|BETTER_AUTH_SECRET)" should also include
DATABASE_URL_UNPOOLED; update the grep/regex used in the if-condition (the
conditional that sets IS_CLIENT_FILE=false) to match
"(DATABASE_URL|DATABASE_URL_UNPOOLED|BETTER_AUTH_SECRET)" (and apply the same
change to the other identical occurrence later in the script around the second
conditional). This ensures lines containing DATABASE_URL_UNPOOLED are treated as
server-only secrets and not flagged as client files.
…etection - Soften absolute "No remaining references to Supabase" verification to allow historical/migration mentions in changelogs - Add root-level path variants (app/*, pages/*, components/*) to security-check.sh client-path detection so files like app/page.tsx are caught without requiring a parent directory prefix - Regenerate init.sh to propagate security-check changes Not changed: DATABASE_URL_UNPOOLED regex — already matched by existing DATABASE_URL substring pattern. Case pattern quoting — would disable glob matching in bash case statements. https://claude.ai/code/session_01Vt7EoDLuUcFLqAwAD155q2
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/AX_UPGRADE_REPORT.md`:
- Around line 48-52: The March 4 historical entry ("2026-03-04 decision entry")
should remain clearly historical and must not be rewritten to imply the
migration occurred twice; revert or edit the bullets that now attribute
Neon/Drizzle/Better Auth guidance to that March 4 entry so they either (a)
restore the original March 4 wording, or (b) add an explicit note that the March
4 bullets are historical and that wording was later normalized after the March
31 upgrade; specifically update the bullets that mention Neon/Drizzle/Better
Auth guidance so they reference the later March 31 change or include a
“normalized after 2026-03-31” clarification rather than implying duplicated
migration.
In `@scripts/init.sh`:
- Around line 231-233: The allow-list in the generated script currently permits
"Bash(pnpm verify*)" and "Bash(yarn verify*)" but the generated CI emits
"${manager} run verify" (e.g., "pnpm run verify" / "yarn run verify"), so update
the source template so the allow-list includes both forms (with and without
"run") — e.g., add "Bash(pnpm run verify*)" and "Bash(yarn run verify*)"
alongside the existing entries that reference "Bash(pnpm verify*)" and
"Bash(yarn verify*)"; then regenerate scripts/init.sh from the template so the
CI workflow will not be blocked by the scaffolded project's allow-list.
- Around line 815-826: The "New Database Migration (Drizzle + Neon)"
documentation block hardcodes pnpm in Steps 2/4 and incorrectly claims
migrations run automatically on deploy (Step 6); update the text to use
portable, non-prescriptive package-manager phrasing (e.g., "npm/yarn/pnpm: run
`drizzle-kit generate`" or "use your package manager: `npm run drizzle-kit
generate` / `pnpm drizzle-kit generate` / `yarn drizzle-kit generate`") for
Steps 2 and 4, and remove or reword Step 6 to avoid asserting automatic
deployment hooks (state that deploy behavior depends on user CI/deploy
configuration and suggest running `drizzle-kit push` or adding a CI step if
desired). Locate and edit the "New Database Migration (Drizzle + Neon)" section
and the Step 2/4/6 text in the CLAUDE.md template emitted by scripts/init.sh to
apply these changes.
- Around line 1589-1595: The fallback scan currently walks only
"$PROJECT_DIR/src" and therefore misses root-level client directories; update
the fallback population logic that builds FILES to also walk root-level app,
pages, and components directories (or generalize the scan to
"$PROJECT_DIR/{src,app,pages,components}") so files like app/*, pages/*, and
components/* are discovered even for manual/non-git runs; locate the code that
iterates the filesystem to populate FILES (the block that references
PROJECT_DIR/src) and extend it to include PROJECT_DIR/app, PROJECT_DIR/pages,
and PROJECT_DIR/components (or a combined glob), ensuring the existing
IS_CLIENT_FILE checks (case ... app/*|pages/*|components/* and the head -5 "use
client" check) still apply.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e5f7e570-4ab6-4f55-abf3-13d5cef09bd8
📒 Files selected for processing (3)
docs/AX_UPGRADE_REPORT.mdscaffold/scripts/security-check.shscripts/init.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- scaffold/scripts/security-check.sh
…rage - Restore March 4 AX_UPGRADE_REPORT bullets to original Supabase-era wording with "(later replaced in 2026-03-31 upgrade)" annotations to avoid implying the migration happened twice - Add missing pnpm/yarn run verify variants to settings.json allow-list so "pnpm run verify" is not blocked by scaffolded permissions - Make migration steps in CLAUDE.md.template package-manager-agnostic and fix step 6 to not claim migrations auto-deploy - Extend security-check.sh fallback file scan to include root-level app/, pages/, components/ directories alongside src/ - Regenerate init.sh to propagate all template changes https://claude.ai/code/session_01Vt7EoDLuUcFLqAwAD155q2
Replace Supabase (database + auth + RLS) with a stronger free-tier combination:
Updates 13 files across scaffold templates, docs, security scripts, and
CLI allow-lists. Security model shifts from database-level RLS to
application-level middleware and server guards.
https://claude.ai/code/session_01Vt7EoDLuUcFLqAwAD155q2