Gallery with thumbnails
A multi-file collection with lazy + eager image conversions, ordering, and a clean render shape.
A post gallery is a multi-file collection where each image needs a couple of variants: a thumb for the grid (lazy — only the ones actually viewed get made) and an og card (eager — ready the moment a crawler hits the page).
Configure
collections: [
{
name: 'gallery',
disk: 's3', // optional: pin this collection to a specific disk
conversions: [
{ name: 'thumb', width: 200 }, // lazy
{ name: 'og', width: 1200, height: 630, eager: true }, // generated on upload
],
},
]Attach (multiple, ordered)
Each attach appends with an incrementing order, so the gallery keeps insertion order:
@Post()
@UseInterceptors(FilesInterceptor('files'))
async add(@Param('id') postId: string, @UploadedFiles() files: Express.Multer.File[]) {
return Promise.all(
files.map((file) =>
this.media.library.attach({
ownerType: 'Post',
ownerId: postId,
collection: 'gallery',
fileName: file.originalname,
mimeType: file.mimetype,
contents: file.buffer,
}),
),
);
}Render
Build exactly the shape your front-end wants. list returns records ordered by order:
async render(postId: string) {
const media = await this.media.library.list('Post', postId, 'gallery');
return Promise.all(
media.map(async (m) => ({
id: m.id,
full: await this.media.library.url(m.id),
thumb: await this.media.library.url(m.id, 'thumb'), // made + cached on first call
width: m.customProperties.width, // whatever you stored at attach time
})),
);
}The og variant exists immediately after upload; thumb is produced on the first request for each image and cached thereafter. Deleting an image removes it and all its variants:
await this.media.library.delete(mediaId);Reordering
Records carry an order field. To support drag-to-reorder, persist new order values through your store (a thin reorder of MediaRecords) — list will return them in the new order on the next read.