Aviary
Packages

@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 | null

Both 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.

On this page