Skip to main content

File Management

Apps store their files (HTML, CSS, JavaScript, images, etc.) in an associated Library. The file management endpoints provide CRUD operations for app content.

Important Concepts

Library Association: Each app has a libraryId field that references its file storage. Apps without a library cannot use file management endpoints.

Path Structure: Files are organized hierarchically using forward-slash paths (e.g., index.html, css/styles.css, images/logo.png).

Content Types: Files have MIME types that determine how they're served. The system auto-detects types from file extensions.

Timestamp Updates: Any write operation (PUT, PATCH, upload) updates the app's updatedAt and defnUpdatedAt timestamps.


GET /api/apps/{id}/contents/{path*}

Read a file from the app's library.

Authentication: Required (session or token)

Permissions Required: Member role (run permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID
pathstringFile path within the library (e.g., index.html, css/styles.css)

Query Parameters:

ParameterTypeDefaultDescription
downloadbooleanfalseForce download instead of inline display

Response:

Returns the file content as a binary stream with appropriate Content-Type header.

Example Request:

GET /api/apps/analytics:sales-dashboard/contents/index.html

Example Response:

HTTP/1.1 200 OK
Content-Type: text/html

<!DOCTYPE html>
<html>
<head><title>Sales Dashboard</title></head>
<body>...</body>
</html>

Download Example:

GET /api/apps/analytics:sales-dashboard/contents/logo.png?download=true
HTTP/1.1 200 OK
Content-Type: image/png
Content-Disposition: attachment; filename="logo.png"

[binary data]

Error Responses:

  • 400 Bad Request - App has no associated library
  • 404 Not Found - App doesn't exist, user lacks access, or file not found
  • 403 Forbidden - User lacks run permission

PUT /api/apps/{id}/contents/{path*}

Write or create a file in the app's library.

Authentication: Required

Permissions Required: Member+ role (write permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID
pathstringFile path within the library

Payload:

{
"content": "<!DOCTYPE html>\n<html>...",
"contentType": "text/html",
"encoding": "utf8"
}

Payload Fields:

FieldTypeRequiredDefaultDescription
contentstringYes-File content (can be empty string)
contentTypestringNoAuto-detectMIME type
encodingstringNoutf8utf8 or base64

Response:

Returns the created/updated file metadata with status 201 Created (new file) or 200 OK (updated file).

{
"id": "file-uuid-1234",
"libraryId": "lib-uuid-5678",
"filename": "index.html",
"directory": false,
"parentId": null,
"contentType": "text/html",
"size": 1234,
"encoding": "utf8",
"createdAt": "2024-02-13T10:00:00.000Z",
"updatedAt": "2024-02-13T10:15:00.000Z"
}

Example - Create HTML File:

PUT /api/apps/analytics:sales-dashboard/contents/index.html
Content-Type: application/json

{
"content": "<!DOCTYPE html>\n<html>\n <head><title>Dashboard</title></head>\n <body><h1>Sales Dashboard</h1></body>\n</html>",
"contentType": "text/html"
}

Example - Upload Binary File (Base64):

PUT /api/apps/analytics:sales-dashboard/contents/logo.png
Content-Type: application/json

{
"content": "iVBORw0KGgoAAAANSUhEUgAAAAUA...",
"contentType": "image/png",
"encoding": "base64"
}

Side Effects:

  • Updates app's updatedAt and defnUpdatedAt timestamps
  • Creates parent directories as needed
  • Overwrites existing file if present

Error Responses:

  • 400 Bad Request - App has no library, or invalid payload
  • 404 Not Found - App doesn't exist or user lacks access
  • 403 Forbidden - User lacks write permission

PATCH /api/apps/{id}/contents/{path*}

Perform a patch operation on an existing file (replace, append, prepend, or insert text).

Authentication: Required

Permissions Required: Member+ role (write permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID
pathstringFile path within the library

Payload:

The payload structure depends on the operation:

Replace Operation

{
"operation": "replace",
"search": "oldText",
"replacement": "newText",
"replaceAll": false
}
FieldTypeRequiredDefaultDescription
operationstringYes-Must be "replace"
searchstringYes-Text to search for
replacementstringYes-Replacement text (can be empty)
replaceAllbooleanNofalseReplace all occurrences (default: first only)

Append Operation

{
"operation": "append",
"text": "Text to add at end"
}

Prepend Operation

{
"operation": "prepend",
"text": "Text to add at beginning"
}

Insert Operation

{
"operation": "insert",
"text": "Text to insert",
"insertAt": 100
}
FieldTypeRequiredDescription
operationstringYesMust be "insert"
textstringYesText to insert
insertAtintegerYesCharacter position (0-based)

Response:

Returns the updated file metadata.

{
"id": "file-uuid-1234",
"libraryId": "lib-uuid-5678",
"filename": "index.html",
"size": 1456,
"updatedAt": "2024-02-13T10:20:00.000Z"
}

Example - Replace Text:

PATCH /api/apps/analytics:sales-dashboard/contents/index.html
Content-Type: application/json

{
"operation": "replace",
"search": "<title>Old Title</title>",
"replacement": "<title>New Title</title>"
}

Example - Append Script Tag:

PATCH /api/apps/analytics:sales-dashboard/contents/index.html
Content-Type: application/json

{
"operation": "append",
"text": "\n<script src=\"analytics.js\"></script>"
}

Side Effects:

  • Updates app's updatedAt and defnUpdatedAt timestamps
  • File must exist (PATCH doesn't create new files)

Error Responses:

  • 400 Bad Request - Invalid operation or missing required fields
  • 404 Not Found - App or file doesn't exist
  • 403 Forbidden - User lacks write permission

GET /api/apps/{id}/files

List all files in the app's library.

Authentication: Required

Permissions Required: Member+ role (write permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID

Response:

Returns an array of file metadata objects.

[
{
"id": "file-uuid-1",
"libraryId": "lib-uuid-5678",
"filename": "index.html",
"directory": false,
"parentId": null,
"contentType": "text/html",
"size": 1234,
"createdAt": "2024-02-13T10:00:00.000Z",
"updatedAt": "2024-02-13T10:15:00.000Z"
},
{
"id": "file-uuid-2",
"libraryId": "lib-uuid-5678",
"filename": "styles.css",
"directory": false,
"parentId": "dir-uuid-1",
"contentType": "text/css",
"size": 567,
"createdAt": "2024-02-13T10:00:00.000Z",
"updatedAt": "2024-02-13T10:00:00.000Z"
},
{
"id": "dir-uuid-1",
"libraryId": "lib-uuid-5678",
"filename": "css",
"directory": true,
"parentId": null,
"createdAt": "2024-02-13T10:00:00.000Z",
"updatedAt": "2024-02-13T10:00:00.000Z"
}
]

Key Fields:

FieldDescription
directorytrue if this is a directory, false for files
parentIdUUID of parent directory (null for root level)
contentTypeMIME type (only present for files)
sizeFile size in bytes (only present for files)

Error Responses:

  • 404 Not Found - App doesn't exist or user lacks access
  • 403 Forbidden - User lacks write permission

DELETE /api/apps/{id}/files/{fileId}

Delete a file or directory from the app's library.

Authentication: Required

Permissions Required: Member+ role (write permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID
fileIdstringFile UUID (from the files list)

Response:

Returns 204 No Content on success.

Behavior:

  • Deleting a directory also deletes all contained files and subdirectories
  • Updates app's updatedAt and defnUpdatedAt timestamps

Error Responses:

  • 404 Not Found - App or file doesn't exist
  • 403 Forbidden - User lacks write permission

POST /api/apps/{id}/_upload

Upload a file using chunked multipart upload. This endpoint is designed for large file uploads from web browsers.

Authentication: Required

Permissions Required: Member+ role (write permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID

Payload:

Multipart form data with file chunks. Refer to the chunked upload implementation for specific field requirements.

Response:

Returns upload status and file metadata.

Use Case:

This endpoint is used by the file upload UI for:

  • Large binary files
  • Progress tracking
  • Resume capability

Error Responses:

  • 404 Not Found - App doesn't exist or user lacks access
  • 403 Forbidden - User lacks write permission

Common File Operations

Create a Simple App

// 1. Create the app
const app = await POST('/api/apps', {
type: 'report',
name: 'My Dashboard'
});

// 2. Add HTML file
await PUT(`/api/apps/${app.id}/contents/index.html`, {
content: '<!DOCTYPE html><html>...',
contentType: 'text/html'
});

// 3. Add CSS file
await PUT(`/api/apps/${app.id}/contents/styles.css`, {
content: 'body { margin: 0; }',
contentType: 'text/css'
});

// 4. Add JavaScript file
await PUT(`/api/apps/${app.id}/contents/app.js`, {
content: 'console.log("Ready");',
contentType: 'application/javascript'
});

Update a File

// Read current content
const response = await GET('/api/apps/analytics:sales-dashboard/contents/index.html');
const currentHTML = await response.text();

// Modify it
const updatedHTML = currentHTML.replace('Old Title', 'New Title');

// Write back
await PUT('/api/apps/analytics:sales-dashboard/contents/index.html', {
content: updatedHTML,
contentType: 'text/html'
});

Bulk Replace Across File

// Replace all occurrences of a string
await PATCH('/api/apps/analytics:sales-dashboard/contents/index.html', {
operation: 'replace',
search: 'oldClassName',
replacement: 'newClassName',
replaceAll: true
});

List and Delete Old Files

// Get all files
const files = await GET('/api/apps/analytics:sales-dashboard/files');

// Find old temp files
const tempFiles = files.filter(f => f.filename.startsWith('temp-'));

// Delete them
for (const file of tempFiles) {
await DELETE(`/api/apps/analytics:sales-dashboard/files/${file.id}`);
}