File and image uploads

Aha! Builder

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() };
});

Top

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 });

Top

Thumbnail variant definitions

FileBlob.thumbnailUrl accepts a ThumbnailDefinition with these options:

Option

Type

Description

resizeToLimit

[width, height]

Downsize to fit within dimensions, keeping aspect ratio. Only shrinks.

resizeToFit

[width, height]

Resize to fit within dimensions, keeping aspect ratio. Shrinks or enlarges.

resizeToFill

[width, height]

Resize to fill dimensions, keeping aspect ratio. Crops the larger dimension if needed.

resizeAndPad

[width, height]

Resize to fit within dimensions, padding the remaining area (transparent or black).

crop

[left, top, width, height]

Extract an area from the image.

rotate

degrees

Rotate the image by the specified angle.

Top

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.

Top

Feedback received!

Error submitting feedback, please try again later