S3-compatible file storage extension for Prisma — store only the key in your database, the extension handles upload, download, signed URLs, and deletion automatically.
bun add @stratapkg/blobPick a driver:
| Import | Requires |
|---|---|
@stratapkg/blob/bun |
Bun built-in Bun.S3Client |
@stratapkg/blob/aws |
@aws-sdk/client-s3 |
@stratapkg/blob/minio |
minio package |
import { PrismaClient } from './generated/client'
import { withBlob } from '@stratapkg/blob'
import { BunS3Driver } from '@stratapkg/blob/bun'
const s3 = new Bun.S3Client({
bucket: 'my-bucket',
region: 'eu-central-1',
accessKeyId: process.env.S3_KEY,
secretAccessKey: process.env.S3_SECRET,
})
const prisma = new PrismaClient({ adapter })
.$extends(withBlob({
driver: new BunS3Driver(s3),
models: {
User: {
avatar: {
prefix: 'avatars',
signedUrl: { expiresIn: 3600 },
},
},
Article: {
cover: {
prefix: 'covers',
},
},
},
}))await prisma.user.create({
data: {
name: 'Alice',
avatar: await Bun.file('avatar.jpg').arrayBuffer(),
// accepts: File | Blob | ArrayBuffer | ReadableStream | Buffer
},
})
// stored in DB: "avatars/1/avatar"
// uploaded to S3: s3://my-bucket/avatars/1/avatarconst user = await prisma.user.findUnique({ where: { id: 1 } })
// user.avatar → "https://s3.../avatars/1/avatar?X-Amz-Signature=..."await prisma.user.update({
where: { id: 1 },
data: { avatar: newFileBuffer },
})
// old file replaced in S3await prisma.user.delete({ where: { id: 1 } })
// record deleted from DB, file deleted from S3Deletion is deferred — the file is removed only after the DB operation succeeds, preventing data loss on transaction rollback.
interface BlobDriver {
put(key: string, data: ArrayBuffer): Promise<void>
get(key: string): Promise<ArrayBuffer>
delete(key: string): Promise<void>
signedUrl(key: string, expiresIn: number): Promise<string>
}- Prisma >= 7.0
- Any Prisma adapter
- S3-compatible storage (AWS S3, Cloudflare R2, MinIO, YC Object Storage...)
MIT