Static Asset -- Usage & Examples
API endpoint specifications, request/response details, and frontend integration examples.
API Endpoints
The component dynamically generates REST endpoints for each configured storage backend. All backends expose the same API structure under their configured basePath.
| Method | Path | Description |
|---|---|---|
GET | /{basePath}/buckets | List all buckets |
GET | /{basePath}/buckets/:bucketName | Get bucket by name |
POST | /{basePath}/buckets/:bucketName | Create a bucket |
DELETE | /{basePath}/buckets/:bucketName | Delete a bucket |
POST | /{basePath}/buckets/:bucketName/upload | Upload files |
GET | /{basePath}/buckets/:bucketName/objects | List objects |
GET | /{basePath}/buckets/:bucketName/objects/:objectName | Stream file |
GET | /{basePath}/buckets/:bucketName/objects/:objectName/download | Download file |
DELETE | /{basePath}/buckets/:bucketName/objects/:objectName | Delete object |
PUT | /{basePath}/buckets/:bucketName/objects/:objectName/meta-links | Sync MetaLink (MetaLink only) |
GET /{basePath}/buckets
Response 200:
[
{ "name": "my-bucket", "creationDate": "2025-01-01T00:00:00.000Z" }
]GET /{basePath}/buckets/:bucketName
Parameters:
bucketName(path): Bucket name
Validation: Bucket name validated with isValidName(). Returns 400 "Invalid bucket name" if invalid.
Response 200:
{ "name": "my-bucket", "creationDate": "2025-01-01T00:00:00.000Z" }Returns null when the bucket does not exist. The response schema is nullable.
POST /{basePath}/buckets/:bucketName
Parameters:
bucketName(path): Name of the new bucket
Validation: Bucket name validated with isValidName(). Returns 400 "Invalid bucket name" if invalid.
Response 200:
{ "name": "my-bucket", "creationDate": "2025-12-13T00:00:00.000Z" }Returns null if bucket creation fails (e.g., already exists). The response schema is nullable.
DELETE /{basePath}/buckets/:bucketName
Parameters:
bucketName(path): Bucket to delete
Validation: Bucket name validated with isValidName(). Returns 400 "Invalid bucket name" if invalid.
Response 200:
{ "isDeleted": true }The isDeleted field is a boolean indicating whether the bucket was successfully removed from storage.
POST /{basePath}/buckets/:bucketName/upload
Parameters:
bucketName(path): Target bucket name
Query Parameters:
principalType(optional, string): Type of the principal to associate with the uploaded files (e.g.,"user","service")principalId(optional, string or number): ID of the principal. Always coerced to a string viaString()before storage regardless of input typevariant(optional, string): Variant tag for the upload (e.g.,"thumbnail","original")
Validation: Bucket name validated with isValidName(). Returns 400 "Invalid bucket name" if invalid.
Request Body: multipart/form-data with file fields. The request body is parsed using the MultipartBodySchema Zod schema:
const MultipartBodySchema = z.object({
files: z.union([z.instanceof(File), z.array(z.instanceof(File))]),
});This accepts either a single File or an array of File objects.
Response 200 (without MetaLink):
[
{
"bucketName": "my-bucket",
"objectName": "file.pdf",
"link": "/assets/buckets/my-bucket/objects/file.pdf"
}
]Response 200 (with MetaLink enabled):
[
{
"bucketName": "my-bucket",
"objectName": "file.pdf",
"link": "/assets/buckets/my-bucket/objects/file.pdf",
"metaLink": {
"id": "uuid",
"bucketName": "my-bucket",
"objectName": "file.pdf",
"link": "/assets/buckets/my-bucket/objects/file.pdf",
"mimetype": "application/pdf",
"size": 1024,
"etag": "abc123",
"metadata": {},
"storageType": "minio",
"isSynced": true,
"variant": "original",
"principalType": "user",
"principalId": "42",
"createdAt": "2025-12-15T03:00:00.000Z",
"modifiedAt": "2025-12-15T03:00:00.000Z"
}
}
]Response 200 (with MetaLink enabled, MetaLink creation failed):
[
{
"bucketName": "my-bucket",
"objectName": "file.pdf",
"link": "/assets/buckets/my-bucket/objects/file.pdf",
"metaLink": null,
"metaLinkError": "Database connection failed"
}
]When MetaLink creation fails, the upload itself still succeeds. The response includes metaLink: null and a metaLinkError string describing the failure. The error is also logged via the controller's scoped logger.
Example:
const formData = new FormData();
formData.append('file', fileBlob, 'document.pdf');
// Upload with principal association and variant
const response = await fetch(
'/assets/buckets/uploads/upload?principalType=user&principalId=123&variant=original',
{ method: 'POST', body: formData },
);
const result = await response.json();
console.log(result[0].metaLink); // Database record (if MetaLink enabled)GET /{basePath}/buckets/:bucketName/objects
Parameters:
bucketName(path): Bucket name
Validation: Bucket name validated with isValidName(). Returns 400 "Invalid bucket name" if invalid.
Query Parameters:
prefix(optional, string): Filter objects by prefix (e.g.,"folder/")recursive(optional, string): Recursive listing. Parsed via strict string comparison=== 'true'-- only the exact string"true"enables recursion; any other truthy value (e.g.,"1","yes") does notmaxKeys(optional, string): Maximum number of objects to return. Parsed as integer viaparseInt(value, 10)
Response 200:
[
{
"name": "file1.pdf",
"size": 1024,
"lastModified": "2025-12-13T00:00:00.000Z",
"etag": "abc123",
"prefix": "folder/"
}
]All fields in the IObjectInfo response are optional. The prefix field is present when listing non-recursively and the object is a directory prefix. When listing individual files, name, size, lastModified, and etag are typically populated.
GET /{basePath}/buckets/:bucketName/objects/:objectName
Parameters:
bucketName(path): Bucket nameobjectName(path): Object name (URL-encoded)
Validation: Both bucket and object names validated with isValidName(). Returns 400 "Invalid bucket name" or "Invalid object name" respectively if either is invalid.
Response:
- Streams file content with appropriate headers
Content-Type: From storage metadata orapplication/octet-streamas fallbackContent-Length: File size in bytesX-Content-Type-Options:nosniff- Additional whitelisted headers forwarded from storage metadata (see Header Sanitization)
GET /{basePath}/buckets/:bucketName/objects/:objectName/download
Parameters:
bucketName(path): Bucket nameobjectName(path): Object name (URL-encoded)
Validation: Both bucket and object names validated with isValidName(). Returns 400 "Invalid bucket name" or "Invalid object name" respectively if either is invalid.
Response:
- Streams file with download headers
Content-Disposition:attachment; filename="..."(generated viacreateContentDispositionHeader())Content-Type: From storage metadata orapplication/octet-streamas fallbackContent-Length: File size in bytesX-Content-Type-Options:nosniff- Additional whitelisted headers forwarded from storage metadata (see Header Sanitization)
- Triggers browser download dialog
Example:
const downloadUrl = `/assets/buckets/uploads/objects/${encodeURIComponent('document.pdf')}/download`;
window.open(downloadUrl, '_blank');DELETE /{basePath}/buckets/:bucketName/objects/:objectName
Parameters:
bucketName(path): Bucket nameobjectName(path): Object to delete (URL-encoded)
Validation: Both bucket and object names validated with isValidName(). Returns 400 "Invalid bucket name" or "Invalid object name" respectively if either is invalid.
Behavior:
- Deletes file from storage
- If MetaLink enabled, the MetaLink database record deletion is fire-and-forget -- the HTTP response returns immediately after the storage delete completes, without awaiting the database deletion
- MetaLink deletion errors are logged but do not fail the request
- MetaLink deletion uses
deleteAll({ where: { bucketName, objectName } })to remove all matching records
Response 200:
{ "success": true }Example:
const bucketName = 'user-uploads';
const objectName = 'document.pdf';
await fetch(`/assets/buckets/${bucketName}/objects/${encodeURIComponent(objectName)}`, {
method: 'DELETE',
});
// File deleted from storage
// MetaLink record deletion initiated (if enabled) but may complete after responsePUT /{basePath}/buckets/:bucketName/objects/:objectName/meta-links
Availability: Only registered when useMetaLink: true.
Parameters:
bucketName(path): Bucket nameobjectName(path): Object name (URL-encoded)
Validation: Both bucket and object names validated with isValidName(). Returns 400 "Invalid bucket name" or "Invalid object name" respectively if either is invalid.
Behavior:
- Fetches current file metadata from storage via
helper.getStat() - Generates the file link using
normalizeLinkFn(or the default link format{basePath}/buckets/{bucket}/objects/{encodedName}) - If MetaLink exists (matched by
bucketName+objectName): Updates with latest metadata viaupdateById(), then refetches viafindById() - If MetaLink doesn't exist: Creates new MetaLink record via
create() - Always sets
isSynced: trueto mark as synchronized
Use Cases:
- Manually sync files that exist in storage but not in database
- Update MetaLink metadata after file changes
- Rebuild MetaLink records after database restore
- Bulk synchronization operations
Response 200 (MetaLink created or updated):
{
"success": true,
"metaLink": {
"id": "uuid",
"bucketName": "user-uploads",
"objectName": "document.pdf",
"link": "/assets/buckets/user-uploads/objects/document.pdf",
"mimetype": "application/pdf",
"size": 1048576,
"etag": "abc123",
"metadata": {},
"storageType": "minio",
"isSynced": true,
"principalType": null,
"principalId": null,
"createdAt": "2025-12-15T03:00:00.000Z",
"modifiedAt": "2025-12-15T03:00:00.000Z"
}
}The response always wraps the MetaLink in a { success: boolean, metaLink: ... } envelope. Both create and update flows return the same shape.
Example:
// Sync a single file
const bucketName = 'user-uploads';
const objectName = 'document.pdf';
const response = await fetch(
`/assets/buckets/${bucketName}/objects/${encodeURIComponent(objectName)}/meta-links`,
{ method: 'PUT' }
);
const result = await response.json();
console.log('Success:', result.success); // true
console.log('Synced:', result.metaLink.isSynced); // true
// Bulk sync example: sync all files in storage
const objects = await fetch(`/assets/buckets/${bucketName}/objects`).then(r => r.json());
for (const obj of objects) {
await fetch(
`/assets/buckets/${bucketName}/objects/${encodeURIComponent(obj.name)}/meta-links`,
{ method: 'PUT' }
);
}Frontend Integration
// Upload file with principal association and variant
async function uploadFile(file: File, principalType?: string, principalId?: string, variant?: string) {
const formData = new FormData();
formData.append('file', file);
const url = new URL('/assets/buckets/user-uploads/upload', window.location.origin);
if (principalType) url.searchParams.append('principalType', principalType);
if (principalId) url.searchParams.append('principalId', principalId);
if (variant) url.searchParams.append('variant', variant);
const response = await fetch(url, {
method: 'POST',
body: formData,
});
const result = await response.json();
return result[0].link;
}
// Download file
function downloadFile(bucketName: string, objectName: string) {
const url = `/assets/buckets/${bucketName}/objects/${encodeURIComponent(objectName)}/download`;
window.open(url, '_blank');
}
// List files in bucket
async function listFiles(bucketName: string, prefix?: string, recursive?: boolean) {
const url = new URL(`/assets/buckets/${bucketName}/objects`, window.location.origin);
if (prefix) url.searchParams.append('prefix', prefix);
if (recursive) url.searchParams.append('recursive', 'true');
const response = await fetch(url);
return await response.json();
}See Also
- Setup & Configuration - Quick Reference, Setup Steps, Configuration Options
- API Reference - Controller Factory, Storage Interface, MetaLink Schema
- Error Reference - Name Validation and Troubleshooting