Aha! Builder applications can accept file and image uploads, store them, and serve them back. Your application can also generate image thumbnails on the fly — resizing, cropping, padding, and rotating images with no extra service to set up. Elle adds file handling when you describe uploads, attachments, or images in your application.
This reference documents the file API: uploading from the client, the server-side FileBlob API, and the full set of thumbnail options. Read it to see what your applications can do with files and images, to review what Elle generated, or to ground an AI assistant in the specifics — reference this article with Elle or any LLM so it knows exactly how file storage works in Aha! Builder.
Click any of the following links to skip ahead:
File uploads
There are two ways to handle file uploads.
Direct upload with uploadFile (recommended)
The preferred way to upload files is to use the uploadFile function on the client. It uploads a File object directly to storage and returns a blob key that can be used with the FileBlob API on the server.
import { uploadFile } from '@aha-app/builder-core';
const handleUpload = async (file: File) => {
const blobId = await uploadFile(file, (percent) => {
console.log(`Upload progress: ${percent}%`);
});
// Pass the blob ID to a server function to store it
await createItem({ name: file.name, imageId: blobId });
};On the server, use FileBlob.loadFileBlob to retrieve the file by its ID:
import { server, db, FileBlob } from '@aha-app/builder-core';
server.data('createItem', async ({ name, imageId }) => {
const item = await db.insert(items).values({ name, imageId }).returning();
const image = FileBlob.loadFileBlob(imageId);
return { id: item[0].id, itemImageUrl: await image.url() };
});Inline binary arguments
If any arguments to a server function, including any values in nested objects or arrays, are of type File | Blob | Uint8Array | ArrayBuffer, they will be converted to FileArgument objects when the server function handler is called.
For example, uploading a file from the client:
const imageFile: File;
createItem("Item name", imageFile);Then on the server call saveAsFileBlob on the FileArgument to persist it and get a FileBlob object:
server.data('createItem', async (name, image) => {
const imageBlob = await image.saveAsFileBlob();
const item = await db.insert(items).values({ name, imageId: imageBlob.id }).returning();
return { id: item[0].id, itemImageUrl: await imageBlob.url() };
});Accessing and managing stored files
The FileBlob API is used to create, read, delete, download and thumbnail stored files and images.
This is a server-side API only.
class FileBlob {
// The unique identifier of this file
readonly id: string;
// The content type of this file.
readonly type: string;
// The name of this file.
readonly name: string;
// Size of the file data in bytes.
readonly size: string;
// Create a FileBlob from a raw array of data or a file in a form encoded upload.
static async createFileBlob(name: string, type: string, data: string | ArrayBuffer | FileArgument): Promise<FileBlob>;
// Load a file blob given its `id` value.
static loadFileBlob(id: string): FileBlob;
// Return a URL that can be used to download the file data.
async url(): Promise<string>;
// Return the raw content of the file as a string.
async text(): Promise<string>;
// Delete the file and its data from storage.
async delete(): Promise<void>;
// Return a URL for a thumbnail of the file. Only applies to image files
// with a type of `image/*`.
async thumbnailUrl(options: ThumbnailDefinition): Promise<string>;
}Examples of working with file blobs:
import { FileBlob } from '@aha-app/builder-core';
// Create a blob
const file = await FileBlob.createFileBlob("export.csv", "text/csv", "col1,col2\nval1,val2");
file.id // ID of the blob. Store in the DB to retrieve it later.
file.type // MIME type
file.name // File name
file.size // File size
// Load an existing blob by ID
const file = FileBlob.loadFileBlob('123');
// Get a download URL
await file.url()
// Read raw text content
await file.text()
// Delete a blob
await file.delete()
// Get a download URL for an image thumbnail
await file.thumbnailUrl({ resizeToLimit: [100, 100], rotate: 45 });Thumbnail variant definitions
FileBlob.thumbnailUrl accepts a ThumbnailDefinition with these options:
Option |
Type |
Description |
|
|
Downsize to fit within dimensions, keeping aspect ratio. Only shrinks. |
|
|
Resize to fit within dimensions, keeping aspect ratio. Shrinks or enlarges. |
|
|
Resize to fill dimensions, keeping aspect ratio. Crops the larger dimension if needed. |
|
|
Resize to fit within dimensions, padding the remaining area (transparent or black). |
|
|
Extract an area from the image. |
|
|
Rotate the image by the specified angle. |
Build it with Elle
Describe the capability you want in plain language, and Elle, the Aha! Builder AI assistant, builds it into your application using the framework above.
New to this? Open Elle, ask what it can do, and try a capability in your application today.