Summary
In apps/backend/src/routes/cards.ts, the POST /api/cards endpoint checks if the user has zero cards to decide whether the new card should be the default:
const cardCount = await app.prisma.card.count({ where: { userId } });
const card = await app.prisma.card.create({
data: {
isDefault: cardCount === 0,
...
}
});
These are two separate database operations with no transaction or lock between them. If two requests arrive simultaneously when a user has zero cards, both will read cardCount === 0 and both will create a card with isDefault: true — leaving the user with two default cards.
Location
apps/backend/src/routes/cards.ts — POST / handler
Steps to Reproduce
- Send two simultaneous
POST /api/cards requests for a new user with no existing cards
- Both requests read
cardCount === 0
- Both cards are created with
isDefault: true
- User now has two default cards data integrity violation
Fix
Wrap the count check and card creation in a single Prisma transaction to prevent the race window.
Impact
Data integrity violation user ends up with multiple default cards causing undefined behaviour in the UI.
Summary
In
apps/backend/src/routes/cards.ts, thePOST /api/cardsendpoint checks if the user has zero cards to decide whether the new card should be the default:These are two separate database operations with no transaction or lock between them. If two requests arrive simultaneously when a user has zero cards, both will read
cardCount === 0and both will create a card withisDefault: true— leaving the user with two default cards.Location
apps/backend/src/routes/cards.ts—POST /handlerSteps to Reproduce
POST /api/cardsrequests for a new user with no existing cardscardCount === 0isDefault: trueFix
Wrap the count check and card creation in a single Prisma transaction to prevent the race window.
Impact
Data integrity violation user ends up with multiple default cards causing undefined behaviour in the UI.