@dudousxd/nestjs-media-database-prisma
Prisma media store — consumer-managed schema, structural client typing.
A MediaStore backed by Prisma. The adapter never imports @prisma/client — Prisma's schema is consumer-managed, so you own the Media model and migrations, and pass your generated PrismaClient.
import { PrismaMediaStore } from '@dudousxd/nestjs-media-database-prisma';
MediaModule.forRoot({ default: 's3', disks: { s3 }, store: new PrismaMediaStore(prisma) });Add the model
Add a Media model to your schema.prisma (map order → a position column):
model Media {
id String @id
ownerType String
ownerId String
collection String
name String
fileName String
mimeType String
size Int
disk String
path String
order Int @map("position")
customProperties Json
conversions Json
createdAt DateTime
updatedAt DateTime
@@index([ownerType, ownerId, collection])
@@map("media")
}Then prisma migrate as usual — the adapter does no schema management.
Structural typing
The store depends on a structural PrismaClientLike interface (just the media delegate methods it calls), not the concrete generated client.
Validated against a real client
The structural contract is verified by an integration suite that generates a real Prisma client and runs the store against real Postgres (testcontainers) — confirming a genuine PrismaClient is assignable, not just an interface that looks right on paper.
Attachment column
Prisma has no entity classes, so the attachment column model is two null-safe helpers around a Json column instead of a decorator. Add a Json? field to your model:
model User {
id String @id
avatar Json?
}Then (de)serialize the Attachment at the boundary:
import { toAttachmentJson, fromAttachmentJson } from '@dudousxd/nestjs-media-database-prisma';
// write
await prisma.user.update({
where: { id },
data: { avatar: toAttachmentJson(avatar) }, // Attachment | null → Json
});
// read
const row = await prisma.user.findUniqueOrFail({ where: { id } });
const avatar = fromAttachmentJson(row.avatar); // Json → Attachment | nullBoth helpers are null-safe, so an absent avatar round-trips as null. (Wrap them in a Prisma result/query extension if you'd rather rehydrate automatically.)
Peer dependencies
@dudousxd/nestjs-media-core only — you bring your own @prisma/client. See Concepts → Persistence and Concepts → Attachments.