-
Notifications
You must be signed in to change notification settings - Fork 4
The Add Files API Flow
This document explains the complete file upload process for the Recollect web app, including client-side and server-side logic, thumbnail handling for videos, and the new JSON-based API structure.
The file upload flow is designed to:
- Handle large files efficiently (bypassing Vercel's 4.5MB serverless limit)
- Support video thumbnail generation and upload
- Use a clean, modern JSON API (no form data)
- Store files and thumbnails in Supabase Storage
- User selects one or more files to upload.
- For video files, a thumbnail is generated using the first frame.
- The
useFileUploadOptimisticMutation
hook handles the upload process with optimistic updates. - Optimistic Update: Immediately adds a placeholder entry to the UI with the file name and current timestamp.
- File Upload: Uses Supabase signed URLs to upload the file directly to storage (bypassing Vercel's 4.5MB limit).
- Thumbnail Upload: If it's a video, uploads the thumbnail to a location.
- Error Handling: If upload fails, the optimistic update is rolled back.
- The main file is uploaded directly to Supabase Storage using a signed URL (client-side, to avoid serverless size limits).
- If the file is a video, the generated thumbnail (as a PNG) is also uploaded to a temporary location in Supabase Storage:
public/{userId}/thumbnail-{uploadFileNamePath}.png
- After upload, the client sends a JSON payload to the API endpoint
/api/file/upload-file
:
{
"category_id": "123",
"name": "video.mp4",
"thumbnailPath": "public/user123/thumbnail-abc123-video.mp4.png",
"type": "video/mp4",
"uploadFileNamePath": "abc123-video.mp4"
}
- For non-video files,
thumbnailPath
isnull
.
- The API receives a JSON payload (not form data).
- It extracts all necessary fields from
request.body
.
- If
thumbnailPath
is provided (i.e., for videos):- The API copies the thumbnail from the location to its final location:
public/{userId}/thumbnail-{uploadFileNamePath}.png
- The thumbnail is deleted from storage.
- The public URL for the thumbnail is generated and used as the
ogImage
. - (Optional) Blurhash and metadata are generated for the thumbnail.
- The API copies the thumbnail from the location to its final location:
- The API inserts a new record into the main table with:
- File URL (from storage)
- Title, type, category, user ID
- Thumbnail URL (if video)
- Metadata (including blurhash, etc.)
- For non-video files, the API may trigger additional processing (e.g., image captioning, OCR, etc.)
- Embeddings are created for search.
- For non-video files, after the initial upload, a separate API call is made to
/api/file/upload-file-remaining-data
. - This API processes the file to extract additional metadata:
- OCR: Extracts text from images using Google's Generative AI
- Image Captioning: Generates descriptions for images
- Blurhash: Creates blur hash for image previews
- The metadata is then updated in the database.
-
Main files:
public/{userId}/{uploadFileNamePath}
-
Video thumbnails :
public/{userId}/thumbnail-{uploadFileNamePath}.png
-
Video thumbnails (final):
public/{userId}/thumbnail-{uploadFileNamePath}.png
Endpoint: POST /api/file/upload-file
Headers:
Content-Type: application/json
Body:
{
"category_id": "123",
"name": "video.mp4",
"thumbnailPath": "public/user123/thumbnail-abc123-video.mp4.png",
"type": "video/mp4",
"uploadFileNamePath": "abc123-video.mp4"
}
The useFileUploadOptimisticMutation
hook provides a smooth user experience by:
- Immediate UI Update: Shows the file in the list before the upload completes
- Background Processing: Handles file upload and API calls in the background
- Error Recovery: Rolls back the UI if the upload fails
- Cache Management: Invalidates and refetches data after successful upload
-
onMutate
: Handles optimistic updates and file uploads -
onError
: Rolls back optimistic updates on failure -
onSettled
: Refreshes data after completion -
onSuccess
: Shows success messages and handles type-specific notifications
const fileUploadOptimisticMutation = useMutation(uploadFile, {
onMutate: async (data) => {
// 1. Cancel outgoing queries
// 2. Snapshot previous data
// 3. Optimistically update UI
// 4. Upload file to Supabase Storage
// 5. Return context for rollback
},
onError: (context) => {
// Rollback optimistic update
},
onSettled: () => {
// Invalidate and refetch data
},
onSuccess: (response, data) => {
// Show success messages
},
});
graph TD
A[User selects file(s)] --> B[Optimistic UI update]
B --> C{Is video?}
C -- No --> D[Upload file to Supabase Storage]
C -- Yes --> E[Generate video thumbnail]
E --> F[Upload thumbnail to location]
F --> G[Upload video file to Supabase Storage]
D & G --> H[Send JSON metadata to API]
H --> I{Is video?}
I -- No --> J[Insert file record in DB]
I -- Yes --> K[Move thumbnail to final location]
K --> L[Generate thumbnail metadata]
L --> M[Insert file + thumbnail record in DB]
J & M --> N[Trigger post-processing]
N --> O{Is non-video?}
O -- Yes --> P[Call remaining data API]
P --> Q[Extract OCR, captions, blurhash]
Q --> R[Update metadata in DB]
O -- No --> S[Complete]
R --> S
Endpoint: POST /api/file/upload-file
Purpose: Handles initial file upload and database insertion
Content-Type: application/json
Endpoint: POST /api/file/upload-file-remaining-data
Purpose: Processes non-video files for additional metadata
Content-Type: application/json
Body:
{
"id": 123,
"publicUrl": "https://example.com/file.jpg"
}
- No form data: All API requests use JSON.
- No file uploads via API: All files (including thumbnails) are uploaded directly to Supabase Storage from the client.
- Efficient for large files: Bypasses Vercel's serverless size limits.
- Optimistic updates: React Query provides immediate UI feedback.
- Clean, modern, and maintainable.
For any questions or improvements, please refer to the code in src/async/uploads/file-upload.ts
, src/pages/api/file/upload-file.ts
, and related files.