Skip to content

feat(api): DELETE /api/account for permanent account removal (#126)#151

Merged
cursor[bot] merged 2 commits into
mainfrom
issue-126-account-deletion
Apr 16, 2026
Merged

feat(api): DELETE /api/account for permanent account removal (#126)#151
cursor[bot] merged 2 commits into
mainfrom
issue-126-account-deletion

Conversation

@auerbachb
Copy link
Copy Markdown
Owner

@auerbachb auerbachb commented Apr 16, 2026

Summary

Part of #126 (App Store Guideline 5.1.1(v)). Adds a server-side endpoint so an authenticated user can permanently delete their account and associated personal data.

Why DELETE /api/account

REST maps the authenticated user’s account to a single resource URI. DELETE is idempotent at the HTTP level (repeat calls after deletion are handled safely) and matches common “delete my account” semantics without a JSON body.

Auth

  • getCurrentUser() from the sp_token cookie; 401 if missing/invalid (middleware + handler).
  • Only the signed-in user’s row is targeted (WHERE id = auth.userId).

Data / transaction

All FKs from users.id in src/db/schema.ts use onDelete: "cascade":
buddy_sessions (host), buddy_session_participants (participant), sessions (owner), thoughts (owner), friend_requests (from/to), friendships (either side).

The implementation runs DELETE FROM users WHERE id = … inside a single Drizzle transaction. Postgres applies cascades in a valid order; no orphaned rows for this user’s owned data.

Co-participants: sessions.buddy_session_idbuddy_sessions is onDelete: "set null". If the deleted user was a buddy host, their buddy_sessions row goes away and other users’ personal sessions rows that pointed at that buddy sit get buddy_session_id nulled (they are not deleted).

Session

JWT is stateless; after a successful delete (or missing user with a stale token), we call clearAuthCookie() (same as /api/auth/logout) so the cookie cannot be reused.

Response

  • 200 { "ok": true } after a row was deleted.
  • 401 unauthenticated.
  • 404 { "error": "Account not found" } if the token user id has no row (cookie still cleared).
  • 500 on unexpected errors (no stack/details in JSON).

Tests

This repo has no npm test / test runner in package.json. Verification for this PR:

  • npm run build (passes).

Manual verification (curl)

  1. Sign up or log in via the app (or POST /api/auth/login) so the browser/client has sp_token.
  2. With the cookie:
    curl -i -X DELETE "http://localhost:3000/api/account" \
      -H "Cookie: sp_token=<paste token value>"
  3. Expect 200 and {"ok":true}, then GET /api/auth/me with the same cookie should yield 401 (cookie cleared on delete response — re-send cookie only if you captured it before the delete response strips it).

Do not commit tokens or .env contents.

Made with Cursor

Summary by CodeRabbit

  • New Features
    • Account deletion endpoint: authenticated users can delete their accounts; successful deletion clears the session and returns success.
    • Clear responses for failure states: returns 401 when unauthenticated, 404 if account not found, and 500 for unexpected server errors.
    • Server logs errors; session clearing is best-effort and won’t block the response.

Implement permanent account deletion in a DB transaction by deleting the
users row; dependent personal data cascades per schema FKs. Clear the
auth cookie after the attempt so JWT cookies cannot be reused.

Part of #126

Made-with: Cursor
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
still-point Ready Ready Preview, Comment Apr 16, 2026 7:57pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d396c9f9-df52-42bd-a93b-79cdc8390c17

📥 Commits

Reviewing files that changed from the base of the PR and between cd96dd7 and 1a4ec8f.

📒 Files selected for processing (1)
  • src/app/api/account/route.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/api/account/route.ts

📝 Walkthrough

Walkthrough

Adds account deletion: a new DELETE /api/account Next.js route that authenticates the user, invokes a transaction-backed deletion function, clears auth cookies (best-effort), and returns 401/404/200/500 based on outcome.

Changes

Cohort / File(s) Summary
API Route
src/app/api/account/route.ts
Added DELETE() route handler that uses getCurrentUser(), calls the deletion helper, attempts to clear auth cookies (best-effort), and returns appropriate HTTP statuses and JSON error payloads. Logs unexpected errors and cookie-clear failures.
Database Deletion Logic
src/lib/accountDeletion.ts
New deleteUserAccount(userId: string): Promise<boolean> which runs a db.transaction to delete from users (Drizzle ORM), uses returning to detect deleted rows, and returns a boolean success flag.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as "API Route\nDELETE /api/account"
    participant Auth as "Auth (getCurrentUser)"
    participant DB as "Database\ndeleteUserAccount"
    Client->>API: DELETE /api/account (with cookies)
    API->>Auth: getCurrentUser()
    alt not authenticated
        Auth-->>API: null
        API-->>Client: 401 { error: "Unauthorized" }
    else authenticated
        Auth-->>API: { userId }
        API->>DB: deleteUserAccount(userId)
        alt deletion failed (not found)
            DB-->>API: false
            API->>API: clear auth cookie (best-effort)
            API-->>Client: 404 { error: "Account not found" }
        else deletion succeeded
            DB-->>API: true
            API->>API: clear auth cookie (best-effort)
            API-->>Client: 200 { ok: true }
        end
    end
    opt unexpected error
        API-->>Client: 500 { error: "Internal server error" }
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped to tidy up an account,

A DELETE, a transaction, a graceful count.
Cookies waved goodbye with a sigh,
Rows vanished beneath my eye.
One soft hop — the record’s gone.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: a new DELETE /api/account endpoint for permanent account removal, which is the primary objective of this pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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 issue-126-account-deletion

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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/api/account/route.ts`:
- Around line 16-27: The current handler treats clearAuthCookie() failures as
fatal even when deleteUserAccount(auth.userId) succeeded; change this so
deletion result is authoritative by making clearAuthCookie() best-effort: after
awaiting deleteUserAccount, if deleted is true attempt clearAuthCookie() inside
its own try/catch and log any cookie-clearing error without returning a 500,
then return the successful JSON response; only propagate a 500 if
deleteUserAccount failed or an unrelated error occurs outside the cookie-clear
catch. Reference deleteUserAccount and clearAuthCookie to locate the changes.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9c31c509-80b8-43d8-b01b-ab30e5ffec4c

📥 Commits

Reviewing files that changed from the base of the PR and between 3c6d76d and cd96dd7.

📒 Files selected for processing (2)
  • src/app/api/account/route.ts
  • src/lib/accountDeletion.ts

Comment thread src/app/api/account/route.ts
Comment thread src/app/api/account/route.ts Outdated
Comment on lines +18 to +23
await clearAuthCookie();

if (!deleted) {
return NextResponse.json({ error: "Account not found" }, { status: 404 });
}

Copy link
Copy Markdown

@vercel vercel Bot Apr 16, 2026

Choose a reason for hiding this comment

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

Auth cookie is cleared on 404 "Account not found" response, causing unexpected logout when deletion fails.

Fix on Vercel

CodeRabbit: avoid turning a successful DB delete into 500 if clearAuthCookie
throws. Vercel review: clear cookie only after we know delete outcome so
stale-token 404 still invalidates the session without implying a row existed.

Co-authored-by: Bretton Auerbach <auerbachb@users.noreply.github.com>
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