File Handling
File Handling Enfyra supports request file uploads, storage-backed file records, metadata updates, and file deletion through dynamic script helpers. Uploaded File Context Multipart requests expose the uploaded request file as $ctx.$uploadedFile or @UPLOADED_FILE . const file = @U
File Handling
Enfyra supports request file uploads, storage-backed file records, metadata updates, and file deletion through dynamic script helpers.
Uploaded File Context
Multipart requests expose the uploaded request file as $ctx.$uploadedFile or @UPLOADED_FILE.
const file = @UPLOADED_FILE;
if (!file) {
@THROW400("File is required");
}
return {
originalname: file.originalname,
mimetype: file.mimetype,
size: file.size,
fieldname: file.fieldname,
};
Available properties:
| Property | Type | Description |
|---|---|---|
originalname |
string |
Filename sent by the client |
mimetype |
string |
Client-provided MIME type |
encoding |
string |
Form upload encoding |
path |
string |
Server temp-file path used internally by Enfyra helpers |
size |
number |
File size in bytes |
fieldname |
string |
Multipart field name, usually file |
Do not read @UPLOADED_FILE.path into a Buffer in script code. For normal request uploads, pass the file object directly to @STORAGE.$upload or @STORAGE.$update so Enfyra streams from disk to the selected storage backend.
Upload Files
Use $ctx.$storage.$upload or @STORAGE.$upload to upload a file to storage and create a file_definition record.
Upload The Request File
if (!@UPLOADED_FILE) {
@THROW400("File is required");
}
const saved = await @STORAGE.$upload({
file: @UPLOADED_FILE,
storageConfig: @BODY.storageConfig,
folder: @BODY.folder,
title: @BODY.title,
description: @BODY.description,
});
return saved;
This path is streaming-safe. Enfyra writes the multipart upload to a temp file, then $storage.$upload({ file }) opens a Readable stream from that temp file. The full file is not buffered into RAM.
Upload A Generated Or Processed File
Use buffer only when the script itself creates or transforms a small file, such as an image thumbnail.
const pngBuffer = await makeThumbnail();
const saved = await @STORAGE.$upload({
filename: "thumbnail.png",
mimetype: "image/png",
buffer: pngBuffer,
size: pngBuffer.length,
description: "Generated thumbnail",
});
return saved;
Do not use the buffer form for large request uploads or database backups. For large files, use the file: @UPLOADED_FILE form.
Upload Options
| Option | Type | Required | Description |
|---|---|---|---|
file |
@UPLOADED_FILE object |
Required for request uploads | Streams the request upload from Enfyra's temp file |
buffer |
Buffer |
Required for generated-file uploads | In-memory bytes for small generated or transformed files |
filename / originalname |
string |
Required for buffer uploads | Filename to store |
mimetype |
string |
Required for buffer uploads | MIME type to store |
size |
number |
Optional | Defaults to request file size or buffer length |
folder |
number \| { id: number } |
No | Folder relation |
storageConfig |
number \| { id: number } |
No | Storage configuration relation |
title |
string |
No | File title |
description |
string |
No | File description |
Pass either file or buffer, never both.
Update Files
Use $ctx.$storage.$update or @STORAGE.$update.
Replace With The Request File
if (!@UPLOADED_FILE) {
@THROW400("File is required");
}
const updated = await @STORAGE.$update(@PARAMS.fileId, {
file: @UPLOADED_FILE,
title: @BODY.title,
description: @BODY.description,
});
return updated;
Metadata-Only Update
const updated = await @STORAGE.$update(@PARAMS.fileId, {
title: @BODY.title,
description: @BODY.description,
folder: @BODY.folder,
});
return updated;
Changing storageConfig requires replacing the blob in the same request. Metadata-only storage moves are rejected because Enfyra cannot point a record at a backend that does not contain the object.
Delete Files
await @STORAGE.$delete(@PARAMS.fileId);
return { success: true };
Deletion removes the file_definition record and the physical object from the configured storage backend.
Validation Pattern
Validate type and size before saving:
const file = @UPLOADED_FILE;
if (!file) {
@THROW400("File is required");
}
const allowed = ["image/jpeg", "image/png", "application/pdf"];
if (!allowed.includes(file.mimetype)) {
@THROW400("Unsupported file type");
}
const maxBytes = 20 * 1024 * 1024;
if (file.size > maxBytes) {
@THROW400("File is too large");
}
return await @STORAGE.$upload({
file,
folder: @BODY.folder,
description: @BODY.description,
});
Register Existing Storage Objects
Use @STORAGE.$registerFile when a trusted external process has already uploaded an object to the selected storage backend and the script only needs to create the file_definition record.
return await @STORAGE.$registerFile({
filename: "backup.sql.gz",
mimetype: "application/gzip",
location: @BODY.location,
size: @BODY.size,
storageConfig: @BODY.storageConfig,
description: @BODY.description,
});
Client Request
Send multipart data with field name file. Do not manually set Content-Type; the browser must set the multipart boundary.
const form = new FormData();
form.append("file", file);
form.append("description", description);
const response = await fetch("/api/upload", {
method: "POST",
credentials: "include",
body: form,
});
Storage Behavior
File uploads use one stream contract internally. Request uploads are disk-backed and streamed to Local, S3, Cloudflare R2, or Google Cloud Storage. The buffer helper form exists only for small generated files and is converted to a stream at the helper edge.
Next Steps
- Context Reference for request context fields.
- Error Handling for
@THROWpatterns. - Repository Methods for querying file records.