fix(backend): Resolve Better Auth user ID / legacy UUID mismatch#135
fix(backend): Resolve Better Auth user ID / legacy UUID mismatch#135
Conversation
The auth middleware was setting userId to Better Auth's opaque string (session.user.id), but domain queries (friends, encounters, circles, etc.) join via auth.users.external_id which is UUID-typed, causing "invalid input syntax for type uuid" errors for new registrations. Fix: resolve the legacy auth.users.external_id in the auth middleware and expose both IDs — userId (legacy UUID) for domain queries and betterAuthId (opaque string) for auth."user" queries (self-profile, preferences). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| const db = c.get('db'); | ||
| const [legacyUser] = await getLegacyExternalIdByEmail.run({ email: session.user.email }, db); | ||
|
|
||
| if (!legacyUser) { |
There was a problem hiding this comment.
getLegacyExternalIdByEmail is called inside authMiddleware, which runs on every protected route. This adds a synchronous DB lookup to every single authenticated request.
Two mitigations to consider:
- Add an index on
auth.users(email)if one doesn't exist — keeps the query fast even at scale. - Longer-term: Consider caching the
email → external_idmapping in the session context or resolving it at registration time (e.g. store the legacyexternal_iddirectly on the Better Auth user record), so the per-request overhead disappears entirely.
There was a problem hiding this comment.
Addressed both items:
- Index:
auth.users(email)already has a UNIQUE constraint and an explicit index (idx_users_email) from the initial schema migration — no additional indexing needed. as anycast: Replaced with{} as unknown as pg.Poolin b1130c5.
Agree on the longer-term approach of resolving the legacy UUID at registration time to eliminate the per-request lookup.
| ## Automated Code Review | Commit | c3fab3c | | Reviewed | 2026-03-15 | | Status | Approved | Critical: 0, Important: 0, Suggestion: 0. All previous issues addressed. Performance: idx_users_email confirmed. Type safety: as-any fixed. Dual-ID split consistently applied. CI: https://github.com/datenknoten/freundebuch/actions/runs/23118484680 |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
No new issues found. All previous review items resolved (idx_users_email index confirmed; as-any cast fixed). The dual-ID approach is sound and consistently applied. CI: https://github.com/datenknoten/freundebuch/actions/runs/23118484680
|
🎉 This PR is included in version 2.67.2 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
session.user.id) asuserExternalIdto domain queries that join viaauth.users.external_id(UUID-typed), causinginvalid input syntax for type uuiderrors for new user registrationsauth.users.external_idby email in the auth middleware and exposes bothuserId(legacy UUID) andbetterAuthId(opaque string) so each query type gets the correct IDbetterAuthId, all domain queries (friends, encounters, circles, etc.) continue usinguserIdwhich is now the correct UUIDTest plan
🤖 Generated with Claude Code