Goal
Currently, every group member must have a registered account (email + auth). This creates friction in real-world use cases:
- You want to track expenses for someone who doesn't use the app (maybe the user will join later)
- A group member rejoins with a different email address (maybe lost email account) and their expense history is lost
- You want to use nicknames per group (people often have different names across friend circles)
The goal is to support guest users — participants in a group who don't necessarily have an account, with the option for a real user to later claim and link to them. Similar Issue was also mentioned here #67 and here #667.
I'd like to implement this and am looking for feedback on the right approach before writing code.
Variant A — Simple isGuestUser flag on the existing User model
Add a single boolean field to User:
model User {
// ...existing fields
isGuestUser Boolean @default(false)
}
How it works:
- When adding group members, allow creating a user with just a name (no email), marked as isGuestUser: true
- On the /join-group page, after authenticating, show a list of unclaimed guest users in the group and offer to claim one
- On claim: transfer all ExpenseParticipant and GroupUser records from the guest user ID to the real user ID, then delete the guest record
Pros:
- Minimal schema change
- Low risk, isolated change
- All existing logic stays the same
Cons:
- User identity is still global — no per-group nicknames
- A person rejoining with a different email still can't reclaim their history without manually claiming the guest
- Once claimed, the link is permanent (no switching)
- Doesn't solve the "same person, different email" problem for already-registered users
Variant B — GroupMember as a first-class entity (recommended)
Introduce a GroupMember table that decouples group participation from user accounts:
model GroupMember {
id Int @id @default(autoincrement())
groupId Int
name String // nickname, freely chosen per group
userId Int? // nullable — linked real account, or null for unlinked
group Group @relation(...)
user User? @relation(...)
@@unique([groupId, userId])
}
ExpenseParticipant would reference groupMemberId instead of userId.
How it works:
- Every person in a group is a GroupMember — with or without an account
- A GroupMember can have any nickname (e.g. "Max" in one group, "Maxi" in another)
- A real User account can link to a GroupMember to "claim" it
- The link can be released or transferred — useful if someone accidentally claims the wrong person
- A user who rejoins with a different email can still claim their GroupMember and recover full expense history
Pros:
- Solves all the problems Variant A has
- Nicknames per group
- Flexible claim/unclaim/switch — no permanent lock-in
- Account-independent expense history — the group's data is stable regardless of who has an account
- More correct mental model: a person in a group is not the same as their online identity
Cons:
- Bigger schema change — ExpenseParticipant references change from userId to groupMemberId
- Balance calculations and DB views need to be updated accordingly
- More implementation effort overall
My preference
Variant B feels like the right long-term design, even though it's more work. Variant A is a quicker win but leaves the core limitation in place.
I'm planning to implement this (or at least attempt it) and would love to know:
- Is Variant B the right direction, or is it too invasive for now?
- Are there any architectural constraints I should be aware of before starting?
@krokosik Happy to discuss and adjust the approach based on your feedback before writing any code.
Goal
Currently, every group member must have a registered account (email + auth). This creates friction in real-world use cases:
The goal is to support guest users — participants in a group who don't necessarily have an account, with the option for a real user to later claim and link to them. Similar Issue was also mentioned here #67 and here #667.
I'd like to implement this and am looking for feedback on the right approach before writing code.
Variant A — Simple isGuestUser flag on the existing User model
Add a single boolean field to User:
How it works:
Pros:
Cons:
Variant B — GroupMember as a first-class entity (recommended)
Introduce a GroupMember table that decouples group participation from user accounts:
ExpenseParticipant would reference groupMemberId instead of userId.
How it works:
Pros:
Cons:
My preference
Variant B feels like the right long-term design, even though it's more work. Variant A is a quicker win but leaves the core limitation in place.
I'm planning to implement this (or at least attempt it) and would love to know:
@krokosik Happy to discuss and adjust the approach based on your feedback before writing any code.