File Endpoints
File upload, download, and management.
Upload File
POST /files/upload
Upload a file.
bash
curl -X POST http://localhost:3000/files/upload \
-H "Authorization: Bearer <token>" \
-F "file=@/path/to/file.pdf" \
-F "entityType=documents" \
-F "entityId=doc-123"Response:
json
{
"success": true,
"file": {
"id": "file-abc123",
"name": "file.pdf",
"size": 1024000,
"mimeType": "application/pdf",
"url": "https://storage.example.com/files/file-abc123",
"entityType": "documents",
"entityId": "doc-123"
}
}Upload Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | Yes | File to upload |
entityType | string | No | Entity type |
entityId | string | No | Entity ID |
public | boolean | No | Public access |
Download File
GET /files/download/:bucket/:path
Download file by path.
bash
curl http://localhost:3000/files/download/bucket/path/to/file.pdf \
-H "Authorization: Bearer <token>" \
--output file.pdfSigned URL
GET /files/signed-url/:bucket/:path
Get signed download URL.
bash
curl "http://localhost:3000/files/signed-url/bucket/path/to/file.pdf?expiresIn=3600" \
-H "Authorization: Bearer <token>"Response:
json
{
"url": "https://storage.example.com/files/file.pdf?token=...",
"expiresAt": "2024-01-01T12:00:00Z"
}Entity Files
GET /files/entity/:entityType/:entityId
List files for entity.
bash
curl http://localhost:3000/files/entity/documents/doc-123 \
-H "Authorization: Bearer <token>"Response:
json
{
"files": [
{
"id": "file-abc123",
"name": "document.pdf",
"size": 1024000,
"mimeType": "application/pdf",
"createdAt": "2024-01-01T00:00:00Z"
}
]
}Delete File
DELETE /files/:fileId
Delete a file.
bash
curl -X DELETE http://localhost:3000/files/file-abc123 \
-H "Authorization: Bearer <token>"Response:
json
{
"success": true,
"deleted": "file-abc123"
}Storage Configuration
json
{
"storageProvider": "supabase",
"supabase": {
"url": "{{env.SUPABASE_URL}}",
"anonKey": "{{env.SUPABASE_ANON_KEY}}"
}
}json
{
"storageProvider": "r2",
"r2": {
"accountId": "{{env.R2_ACCOUNT_ID}}",
"accessKeyId": "{{env.R2_ACCESS_KEY_ID}}",
"secretAccessKey": "{{env.R2_SECRET_ACCESS_KEY}}",
"bucket": "my-bucket",
"publicUrl": "https://my-bucket.r2.dev"
}
}File Metadata Storage
File metadata is stored in your configured database for querying and access control.
sql
-- tenant_assets table schema
CREATE TABLE tenant_assets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL,
bucket TEXT NOT NULL,
path TEXT NOT NULL,
filename TEXT,
content_type TEXT,
size INTEGER,
entity_type TEXT,
entity_id TEXT,
uploaded_by TEXT,
uploaded_at TIMESTAMPTZ DEFAULT NOW()
);
-- Query files for entity
SELECT * FROM tenant_assets
WHERE tenant_id = 'tenant-123'
AND entity_type = 'documents'
AND entity_id = 'doc-123';js
// Firestore collection: tenant_assets
{
tenant_id: "tenant-123",
bucket: "my-bucket",
path: "tenant-123/documents/doc-123/1704067200000-file.pdf",
filename: "file.pdf",
content_type: "application/pdf",
size: 1024000,
entity_type: "documents",
entity_id: "doc-123",
uploaded_by: "user-456",
uploaded_at: Timestamp
}
// Query files for entity
db.collection('tenant_assets')
.where('tenant_id', '==', 'tenant-123')
.where('entity_type', '==', 'documents')
.where('entity_id', '==', 'doc-123')
.get()sql
-- tenant_assets table schema
CREATE TABLE tenant_assets (
id TEXT PRIMARY KEY,
tenant_id TEXT NOT NULL,
bucket TEXT NOT NULL,
path TEXT NOT NULL,
filename TEXT,
content_type TEXT,
size INTEGER,
entity_type TEXT,
entity_id TEXT,
uploaded_by TEXT,
uploaded_at TEXT DEFAULT (datetime('now'))
);
-- Query files for entity
SELECT * FROM tenant_assets
WHERE tenant_id = 'tenant-123'
AND entity_type = 'documents'
AND entity_id = 'doc-123';Route Examples
Define file-related routes in your config:
json
{
"path": "/projects/:projectId/files",
"method": "GET",
"supabaseQueries": [{
"table": "tenant_assets",
"operation": "select",
"select": "id, filename, content_type, size, uploaded_at",
"filters": [
{ "column": "entity_type", "operator": "eq", "value": "projects" },
{ "column": "entity_id", "operator": "eq", "value": "{{params.projectId}}" }
],
"order": { "column": "uploaded_at", "ascending": false }
}]
}json
{
"path": "/projects/:projectId/files",
"method": "GET",
"firebaseQueries": [{
"collection": "tenant_assets",
"operation": "query",
"filters": [
{ "field": "entity_type", "operator": "==", "value": "projects" },
{ "field": "entity_id", "operator": "==", "value": "{{params.projectId}}" }
],
"orderBy": { "field": "uploaded_at", "direction": "desc" }
}]
}json
{
"path": "/projects/:projectId/files",
"method": "GET",
"supabaseQueries": [{
"table": "tenant_assets",
"operation": "select",
"select": "id, filename, content_type, size, uploaded_at",
"filters": [
{ "column": "entity_type", "operator": "eq", "value": "projects" },
{ "column": "entity_id", "operator": "eq", "value": "{{params.projectId}}" }
],
"order": { "column": "uploaded_at", "ascending": false }
}]
}Path Structure
Files are stored with tenant isolation:
{tenantId}/{entityType}/{entityId}/{timestamp}-{filename}Example: PVqyIY2BHiH8khPFEAdc/documents/doc-123/1704067200000-report.pdf
Presigned URLs
Presigned URLs use S3 Signature V4 for secure, time-limited access.
| Endpoint | Default Expiry |
|---|---|
/files/download/* | 5 minutes |
/files/signed-url/* | 30 seconds (max) |
/files/upload-json | 24 hours |
Access Control
Configure via fileAccessControl.mode:
| Mode | Behavior |
|---|---|
user_only | Only uploader can access |
user_or_admin | Uploader or admin roles |
tenant_shared | All tenant users (default) |
rbac | Role-based via allowedRoles |
json
{
"fileAccessControl": {
"mode": "user_or_admin",
"adminRoles": ["admin", "super_admin"]
}
}File Size Limits
Default: 10MB
Configured via body limit middleware.
Supported Types
All file types supported. Common types:
- Documents: PDF, DOCX, TXT
- Images: PNG, JPG, GIF, SVG
- Data: JSON, CSV, XML
- Archives: ZIP, TAR