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) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-12 10:42:42 +02:00
parent a07e791368
commit 33a1ed9cc4
+22 -17
View File
@@ -50,23 +50,28 @@ export async function POST(request: Request) {
return Response.json({ error: "MusicBrainz unavailable" }, { status: 502 }); return Response.json({ error: "MusicBrainz unavailable" }, { status: 502 });
} }
const artist = await prisma.watchedArtist.create({ data: { mbid, name: name.trim() } }); // Create the artist and its discography atomically so a mid-write failure can't leave
if (releases.length > 0) { // a followed artist with no releases (which the 409 guard above would then wedge).
await prisma.monitoredRelease.createMany({ const artist = await prisma.$transaction(async (tx) => {
data: releases.map((r) => ({ const created = await tx.watchedArtist.create({ data: { mbid, name: name.trim() } });
watchedArtistId: artist.id, if (releases.length > 0) {
artistMbid: mbid, await tx.monitoredRelease.createMany({
artistName: name.trim(), data: releases.map((r) => ({
rgMbid: r.rgMbid, watchedArtistId: created.id,
album: r.title, artistMbid: mbid,
primaryType: r.primaryType, artistName: name.trim(),
secondaryTypes: r.secondaryTypes, rgMbid: r.rgMbid,
firstReleaseDate: r.firstReleaseDate, album: r.title,
monitored: false, primaryType: r.primaryType,
})), secondaryTypes: r.secondaryTypes,
skipDuplicates: true, firstReleaseDate: r.firstReleaseDate,
}); monitored: false,
} })),
skipDuplicates: true,
});
}
return created;
});
return Response.json( return Response.json(
{ id: artist.id, mbid: artist.mbid, name: artist.name, releaseCount: releases.length }, { id: artist.id, mbid: artist.mbid, name: artist.name, releaseCount: releases.length },
{ status: 201 }, { status: 201 },