| path | docs/api/Media_API.mdx | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| title | Media API Reference | |||||||||
| description | Complete API reference for uploading, managing, and serving media files in SveltyCMS with support for local and cloud storage. | |||||||||
| order | 5 | |||||||||
| icon | mdi:image-multiple | |||||||||
| author | admin | |||||||||
| created | 2025-10-05 | |||||||||
| updated | 2025-11-06 | |||||||||
| tags |
|
The Media API provides a robust and type-safe system for managing media files (images, videos, documents) in SveltyCMS. All operations are handled by the MediaService, which uses a database-agnostic adapter and supports multi-tenancy.
Storage Support:
- Local Storage: Files stored on the server filesystem.
- Cloud Storage: S3, Cloudflare R2, or Cloudinary.
Base Path: /api/media
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/media/process |
Upload files |
| GET | /api/media |
List media files |
| GET | /api/media/[id] |
Get media details |
| PATCH | /api/media/[id] |
Update metadata |
| DELETE | /api/media/[id] |
Delete media |
| POST | /api/media/search |
Advanced search |
| GET | /api/media/search |
Search suggestions |
| POST | /api/media/bulk-download |
Download as archive |
| POST | /api/media/trash |
Soft delete |
| POST | /api/media/manipulate/[id] |
Transform media |
| GET | /api/media/exists |
Check existence |
| POST | /api/media/remote |
Upload from URL |
When a file is uploaded, it is stored as a MediaItem document in the database. This provides rich metadata and versioning.
interface MediaItem {
_id: string; // Unique database ID
filename: string; // Original filename
hash: string; // SHA-256 hash of the file content
path: string; // Relative storage path (e.g., "global/original/image-hash.jpg")
url: string; // Publicly accessible URL
mimeType: string; // File MIME type
size: number; // File size in bytes
createdBy: string; // ID of the user who uploaded the file
createdAt: string; // ISODateString of creation time
updatedAt: string; // ISODateString of last update time
thumbnails: {
// Auto-generated for images (except SVG)
[key: string]: {
url: string;
width: number;
height: number;
};
};
metadata: {
// Additional metadata
originalFilename: string;
uploadedBy: string;
uploadTimestamp: string;
[key: string]: any;
};
access: MediaAccess[]; // Access control rules
}SveltyCMS supports multiple storage backends configured via system settings.
local- Files stored on the server filesystem (default).s3- Amazon S3 or other S3-compatible services.r2- Cloudflare R2 (which is S3-compatible).cloudinary- Cloudinary media platform.
// System Settings
{
"MEDIA_STORAGE_TYPE": "local",
"MEDIA_FOLDER": "./mediaFolder" // Filesystem path relative to the project root
}For S3-compatible storage, a bucket name is required. The MEDIA_FOLDER acts as a prefix (subfolder) within the bucket.
// System Settings
{
"MEDIA_STORAGE_TYPE": "s3", // or "r2"
"MEDIA_BUCKET_NAME": "your-s3-bucket-name", // **Required**: The name of your S3 bucket.
"MEDIA_FOLDER": "cms-media", // Optional: A prefix to organize files within the bucket.
"MEDIA_CLOUD_REGION": "us-east-1",
"MEDIA_CLOUD_ENDPOINT": "https://s3.amazonaws.com", // Optional for AWS S3, required for other providers.
"MEDIA_CLOUD_PUBLIC_URL": "https://cdn.example.com" // The public URL of your bucket/CDN.
}// System Settings
{
"MEDIA_STORAGE_TYPE": "cloudinary",
"MEDIA_FOLDER": "cms-media" // A folder name within your Cloudinary account.
}
// Environment Variables (required)
CLOUDINARY_CLOUD_NAME=your-cloud-name
CLOUDINARY_API_KEY=your-api-key
CLOUDINARY_API_SECRET=your-api-secretAll media endpoints require an authenticated session via a cookie.
Cookie: session=your-session-idUploads one or more files. The files are processed by MediaService and saved as MediaItem documents.
POST /api/media/processHeaders:
Content-Type: multipart/form-dataBody (multipart/form-data):
files(File): One or more files to upload.folder(string, optional): A virtual folder path to associate the media with.alt(string, optional): Alt text for images.
Permissions Required: media:write or authenticated user.
A MediaItem object for each successfully uploaded file.
{
"success": true,
"files": [
{
"_id": "db_id_string",
"filename": "photo.jpg",
"hash": "a1b2c3d4e5f6",
"path": "global/original/photo-a1b2c3d4e5f6.jpg",
"url": "https://cdn.example.com/global/original/photo-a1b2c3d4e5f6.jpg",
"mimeType": "image/jpeg",
"size": 123456,
"createdBy": "user_id_string",
"createdAt": "2025-11-06T10:00:00.000Z",
"updatedAt": "2025-11-06T10:00:00.000Z",
"thumbnails": {
"small": { "url": "...", "width": 400, "height": 300 },
"medium": { "url": "...", "width": 800, "height": 600 }
},
"metadata": {
"originalFilename": "photo.jpg",
"uploadedBy": "user_id_string",
"uploadTimestamp": "2025-11-06T10:00:00.000Z"
}
}
],
"message": "Files uploaded successfully"
}Retrieves a paginated list of MediaItem documents.
GET /api/mediaQuery Parameters:
limit(number, optional): Max files per page (default: 20).page(number, optional): Page number to retrieve (default: 1).folder(string, optional): Filter by virtual folder path.type(string, optional): Filter by MIME type (e.g.,image/jpeg).
Permissions Required: media:read or authenticated user.
{
"success": true,
"media": [
// Array of MediaItem objects
],
"total": 42
}Retrieves a single MediaItem by its ID.
GET /api/media/[id]Permissions Required: media:read or authenticated user.
A single MediaItem object.
{
"success": true,
"data": {
// A MediaItem object
}
}Updates the metadata of a MediaItem. This does not change the file itself.
PATCH /api/media/[id]Body (JSON):
{
"filename": "A Better Name.jpg",
"metadata": {
"alt": "A descriptive alt text.",
"tags": ["nature", "mountain"]
}
}Permissions Required: media:update or file owner.
{
"success": true,
"message": "Media file updated successfully"
}Permanently deletes a media file from both the database and the storage provider (local or cloud).
DELETE /api/media/[id]Permissions Required: media:delete or admin.
{
"success": true,
"message": "Media deleted successfully"
}Search media files using multiple criteria including dimensions, dates, EXIF data, file properties, and more.
POST /api/media/search
Content-Type: application/jsonBody (JSON):
{
"criteria": {
"filename": "photo",
"tags": ["nature", "landscape"],
"minWidth": 1920,
"maxWidth": 3840,
"minHeight": 1080,
"aspectRatio": "landscape",
"minSize": 1048576,
"maxSize": 10485760,
"fileTypes": ["image/jpeg", "image/png"],
"uploadedAfter": "2024-01-01T00:00:00.000Z",
"uploadedBefore": "2024-12-31T23:59:59.999Z",
"hasEXIF": true,
"camera": "Canon",
"location": "New York",
"dominantColor": "#FF5733",
"showDuplicatesOnly": false,
"hashMatch": "a1b2c3d4e5f6"
}
}Search Criteria Options:
| Field | Type | Description |
|---|---|---|
filename |
string | Partial filename match (case-insensitive) |
tags |
string[] | Array of tags (all must match) |
minWidth |
number | Minimum image width in pixels |
maxWidth |
number | Maximum image width in pixels |
minHeight |
number | Minimum image height in pixels |
maxHeight |
number | Maximum image height in pixels |
aspectRatio |
'landscape' | 'portrait' | 'square' | Image aspect ratio |
minSize |
number | Minimum file size in bytes |
maxSize |
number | Maximum file size in bytes |
fileTypes |
string[] | Array of MIME types |
uploadedAfter |
Date | Files uploaded after this date |
uploadedBefore |
Date | Files uploaded before this date |
hasEXIF |
boolean | Filter by EXIF data presence |
camera |
string | Camera make/model (partial match) |
location |
string | Location from EXIF data |
dominantColor |
string | Dominant color (hex format) |
showDuplicatesOnly |
boolean | Show only duplicate files |
hashMatch |
string | Exact hash match |
Permissions Required: media:read or authenticated user.
{
"success": true,
"files": [
{
"_id": "file_id_1",
"filename": "photo-nature.jpg",
"hash": "a1b2c3d4e5f6",
"width": 1920,
"height": 1080,
"size": 2458624,
"mimeType": "image/jpeg",
"tags": ["nature", "landscape"]
}
],
"totalCount": 42,
"matchedCriteria": ["filename: \"photo\"", "tags: nature, landscape", "minWidth: 1920px", "aspectRatio: landscape"]
}Retrieve search suggestions based on existing media files (tags, cameras, common dimensions).
GET /api/media/searchPermissions Required: media:read or authenticated user.
{
"success": true,
"suggestions": {
"tags": ["nature", "landscape", "portrait", "urban"],
"cameras": ["Canon EOS 5D", "Nikon D850", "Sony A7III"],
"dimensions": [
{ "width": 1920, "height": 1080 },
{ "width": 3840, "height": 2160 }
],
"sizesRanges": [
{ "min": 0, "max": 102400, "label": "< 100 KB" },
{ "min": 102400, "max": 1048576, "label": "100 KB - 1 MB" },
{ "min": 1048576, "max": 5242880, "label": "1 MB - 5 MB" },
{ "min": 5242880, "max": 10485760, "label": "5 MB - 10 MB" },
{ "min": 10485760, "max": null, "label": "> 10 MB" }
]
}
}Download multiple media files as a single TAR.GZ archive.
POST /api/media/bulk-download
Content-Type: application/jsonBody (JSON):
{
"fileIds": ["file_id_1", "file_id_2", "file_id_3"]
}Permissions Required: media:read or authenticated user.
Binary TAR.GZ stream with headers:
Content-Type: application/gzip
Content-Disposition: attachment; filename="media_files_2024-11-15_143025.tar.gz"
Content-Length: 12582912Implementation Details:
- Uses pure Node.js TAR format (no external dependencies)
- Gzip compression level 6
- Automatic cleanup after download (5 seconds)
- Memory-efficient streaming
- Files organized by folder structure in archive
Example Usage (Client-side):
const response = await fetch('/api/media/bulk-download', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fileIds: ['id1', 'id2'] })
});
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'media_files.tar.gz';
a.click();
URL.revokeObjectURL(url);Move a media file to trash without permanent deletion.
POST /api/media/trash
Content-Type: application/jsonBody (JSON):
{
"fileId": "file_id_to_trash"
}Permissions Required: media:delete or file owner.
{
"success": true,
"message": "File moved to trash",
"trashPath": ".trash/global/original/image-hash.jpg"
}Note: Files in trash can be manually restored from the filesystem. Trash cleanup is handled by system maintenance tasks.
Transform or edit an existing image (resize, crop, rotate, apply filters).
POST /api/media/manipulate/[id]
Content-Type: application/jsonBody (JSON):
{
"operations": [
{
"type": "resize",
"width": 800,
"height": 600,
"fit": "cover"
},
{
"type": "rotate",
"angle": 90
},
{
"type": "blur",
"sigma": 5
}
],
"outputFormat": "webp",
"quality": 85
}Supported Operations:
resize- Change dimensionscrop- Crop to specific arearotate- Rotate by angleflip- Flip horizontal/verticalblur- Apply Gaussian blursharpen- Sharpen imagegrayscale- Convert to grayscaletint- Apply color tint
Permissions Required: media:update or file owner.
{
"success": true,
"message": "Image manipulated successfully",
"newFile": {
"_id": "new_file_id",
"url": "https://cdn.example.com/manipulated-image.webp"
}
}Check if a file exists by hash or filename without fetching full metadata.
GET /api/media/exists?hash=a1b2c3d4e5f6Or by filename:
GET /api/media/exists?filename=photo.jpgPermissions Required: media:read or authenticated user.
{
"success": true,
"exists": true,
"fileId": "existing_file_id",
"filename": "photo.jpg"
}Use Cases:
- Duplicate prevention during upload
- File verification before operations
- Client-side validation
Upload a file from a remote URL (saves a reference for remote videos, downloads and saves for other files).
POST /api/media/remote
Content-Type: application/jsonBody (JSON):
{
"url": "https://example.com/image.jpg",
"access": "public"
}Permissions Required: media:write or authenticated user.
{
"success": true,
"file": {
"_id": "new_file_id",
"filename": "image.jpg",
"url": "https://cdn.example.com/global/original/image-hash.jpg",
"mimeType": "image/jpeg",
"size": 245862
}
}Supported URLs:
- Direct image/video/document URLs
- YouTube videos (reference only)
- Vimeo videos (reference only)
- Any publicly accessible HTTP/HTTPS URL
All endpoints may return standard error responses:
{
"success": false,
"error": "Invalid request parameters",
"details": "File size exceeds 50MB limit"
}{
"success": false,
"error": "Authentication required"
}{
"success": false,
"error": "Insufficient permissions",
"required": "media:write"
}{
"success": false,
"error": "Media file not found",
"fileId": "requested_file_id"
}{
"success": false,
"error": "Server error occurred",
"message": "Failed to process image"
}API endpoints are rate-limited per user:
- Upload endpoints: 10 requests/minute
- Read endpoints: 100 requests/minute
- Delete endpoints: 20 requests/minute
- Bulk operations: 5 requests/minute
Rate Limit Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1638360000For more information about the media system, see:
- Media Gallery Guide - User interface and features
- Media Gallery Structure - Architecture and components
- Media Gallery Implementation - Implementation details
- Media Handling - Storage configuration and MediaService
Complete API Endpoints Summary:
All 12 media API endpoints documented in this reference:
- ✅
POST /api/media/process- Upload files - ✅
GET /api/media- List media files - ✅
GET /api/media/[id]- Get media details - ✅
PATCH /api/media/[id]- Update metadata - ✅
DELETE /api/media/[id]- Delete media - ✅
POST /api/media/search- Advanced search with 18+ criteria - ✅
GET /api/media/search- Search suggestions (tags, cameras, dimensions) - ✅
POST /api/media/bulk-download- Bulk download as TAR.GZ archive - ✅
POST /api/media/trash- Soft delete to trash - ✅
POST /api/media/manipulate/[id]- Image manipulation (resize, crop, rotate, filters) - ✅
GET /api/media/exists- Check file existence by hash or filename - ✅
POST /api/media/remote- Upload from remote URL