Skip to content

feat: upgrade free-tier stack from Supabase to Neon + Better Auth + Drizzle#7

Merged
jeffgreendesign merged 4 commits intomainfrom
claude/upgrade-free-tier-stack-SuFTC
Mar 31, 2026
Merged

feat: upgrade free-tier stack from Supabase to Neon + Better Auth + Drizzle#7
jeffgreendesign merged 4 commits intomainfrom
claude/upgrade-free-tier-stack-SuFTC

Conversation

@jeffgreendesign
Copy link
Copy Markdown
Owner

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

…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
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 516622f6-36f6-4efb-a044-ca55bcb969bc

📥 Commits

Reviewing files that changed from the base of the PR and between 2f4f3a2 and c6121f3.

📒 Files selected for processing (5)
  • docs/AX_UPGRADE_REPORT.md
  • scaffold/.claude/settings.json
  • scaffold/CLAUDE.md.template
  • scaffold/scripts/security-check.sh
  • scripts/init.sh
✅ Files skipped from review due to trivial changes (1)
  • scaffold/CLAUDE.md.template
🚧 Files skipped from review as they are similar to previous changes (2)
  • scaffold/scripts/security-check.sh
  • scaffold/.claude/settings.json

Walkthrough

This 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)
Loading
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
Loading
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: upgrading the free-tier stack from Supabase to Neon + Better Auth + Drizzle, which is clearly reflected throughout all 13 modified files.
Description check ✅ Passed The description is directly related to the changeset, explaining the stack replacement (Supabase to Neon + Better Auth + Drizzle + Vercel Blob) and documenting the security model shift from RLS to application-level controls.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/upgrade-free-tier-stack-SuFTC

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-kit commands only have pnpm variants. 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

📥 Commits

Reviewing files that changed from the base of the PR and between de7c414 and 43daf91.

📒 Files selected for processing (14)
  • AGENTS.md
  • CLAUDE.md
  • docs/ARCHITECTURE.md
  • docs/AX_UPGRADE_REPORT.md
  • docs/DECISIONS.md
  • docs/ENV.md
  • docs/RUNBOOK.md
  • docs/TROUBLESHOOTING.md
  • scaffold/.claude/settings.json
  • scaffold/.env.example
  • scaffold/AGENTS.md.template
  • scaffold/CLAUDE.md.template
  • scaffold/scripts/security-check.sh
  • scripts/init.sh

Comment thread docs/AX_UPGRADE_REPORT.md Outdated
Comment thread scripts/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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 43daf91 and a66fba8.

📒 Files selected for processing (4)
  • docs/AX_UPGRADE_REPORT.md
  • scaffold/.claude/settings.json
  • scaffold/scripts/security-check.sh
  • scripts/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

Comment thread docs/AX_UPGRADE_REPORT.md Outdated
Comment on lines +122 to 123
if echo "$line" | grep -qE "(DATABASE_URL|BETTER_AUTH_SECRET)" 2>/dev/null; then
IS_CLIENT_FILE=false
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment thread scaffold/scripts/security-check.sh Outdated
…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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between a66fba8 and 2f4f3a2.

📒 Files selected for processing (3)
  • docs/AX_UPGRADE_REPORT.md
  • scaffold/scripts/security-check.sh
  • scripts/init.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • scaffold/scripts/security-check.sh

Comment thread docs/AX_UPGRADE_REPORT.md Outdated
Comment thread scripts/init.sh
Comment thread scripts/init.sh
Comment thread scripts/init.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
@jeffgreendesign jeffgreendesign merged commit 11863a5 into main Mar 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants