From 33a1ed9cc401b0c88af4b64e4d5cdb7fc75d330e Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 12 Jul 2026 10:42:42 +0200 Subject: [PATCH] fix(artists-api): create artist + discography atomically Wrap the WatchedArtist.create + MonitoredRelease.createMany in a $transaction so a mid-write failure can't leave a followed artist with no releases (a state the 409 duplicate-follow guard would then wedge). Co-Authored-By: Claude Opus 4.8 (1M context) --- web/src/app/api/artists/route.ts | 39 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/web/src/app/api/artists/route.ts b/web/src/app/api/artists/route.ts index b80820e..db206af 100644 --- a/web/src/app/api/artists/route.ts +++ b/web/src/app/api/artists/route.ts @@ -50,23 +50,28 @@ export async function POST(request: Request) { return Response.json({ error: "MusicBrainz unavailable" }, { status: 502 }); } - const artist = await prisma.watchedArtist.create({ data: { mbid, name: name.trim() } }); - if (releases.length > 0) { - await prisma.monitoredRelease.createMany({ - data: releases.map((r) => ({ - watchedArtistId: artist.id, - artistMbid: mbid, - artistName: name.trim(), - rgMbid: r.rgMbid, - album: r.title, - primaryType: r.primaryType, - secondaryTypes: r.secondaryTypes, - firstReleaseDate: r.firstReleaseDate, - monitored: false, - })), - skipDuplicates: true, - }); - } + // Create the artist and its discography atomically so a mid-write failure can't leave + // a followed artist with no releases (which the 409 guard above would then wedge). + const artist = await prisma.$transaction(async (tx) => { + const created = await tx.watchedArtist.create({ data: { mbid, name: name.trim() } }); + if (releases.length > 0) { + await tx.monitoredRelease.createMany({ + data: releases.map((r) => ({ + watchedArtistId: created.id, + artistMbid: mbid, + artistName: name.trim(), + rgMbid: r.rgMbid, + album: r.title, + primaryType: r.primaryType, + secondaryTypes: r.secondaryTypes, + firstReleaseDate: r.firstReleaseDate, + monitored: false, + })), + skipDuplicates: true, + }); + } + return created; + }); return Response.json( { id: artist.id, mbid: artist.mbid, name: artist.name, releaseCount: releases.length }, { status: 201 },