Correct File Upload and MinIO Flow for CV Extraction
Overviewโ
The correct flow for CV extraction uses your existing GraphQL infrastructure and MinIO storage, not a custom upload API. Here's how it works:
๐ Complete Flowโ
1. Generate Upload URL (GraphQL)โ
const { data } = await generateUploadUrl({
variables: {
filename: file.name,
contentType: file.type
}
})
const { uploadUrl, objectPath } = data.upload.generateUploadUrl
What happens:
- Your GraphQL server generates a signed MinIO upload URL
- Returns both the
uploadUrl(for uploading) andobjectPath(for referencing)
2. Upload File to MinIO Directlyโ
const xhr = new XMLHttpRequest()
xhr.open('PUT', uploadUrl)
xhr.setRequestHeader('Content-Type', file.type)
xhr.send(file) // Upload directly to MinIO
What happens:
- File is uploaded directly to MinIO using the signed URL
- No file goes through your API server
- Progress tracking available via XMLHttpRequest
3. Send Object Path via WebSocketโ
const message = {
type: 'cv_extraction_request',
pipeline_type: 'cv_extraction',
payload: {
id: requestId,
file_url: objectPath // โ This is the MinIO object path, not a URL
}
}
What happens:
- Send the
objectPath(like"cv-files/1234567890-resume.pdf") to the pipeline - Pipeline worker can access the file directly from MinIO using this path
๐๏ธ Architecture Benefitsโ
Why This Approach is Betterโ
- No API Server Load: Files don't go through your API server
- Direct MinIO Access: Pipeline workers read directly from MinIO
- Signed URLs: Secure, time-limited upload permissions
- Progress Tracking: Real-time upload progress
- Existing Infrastructure: Uses your current GraphQL setup
Data Flow Diagramโ
Client ----[1. Generate URL]----> GraphQL Server
<---[uploadUrl, objectPath]--
Client ----[2. Upload File]------> MinIO Storage
<---[Upload Complete]------
Client ----[3. WebSocket Message]--> Pipeline Server
| (with objectPath)
|
<---[4. Processing Results]---
๐ง Implementation Detailsโ
GraphQL Mutation Usedโ
mutation GenerateUploadUrl($filename: String!, $contentType: String) {
upload {
generateUploadUrl(filename: $filename, contentType: $contentType) {
uploadUrl # For uploading to MinIO
objectPath # For referencing in pipeline
}
}
}
WebSocket Message Formatโ
{
"type": "cv_extraction_request",
"pipeline_type": "cv_extraction",
"payload": {
"id": "extract-1703123456-abc123",
"file_url": "cv-files/1703123456-john_doe_resume.pdf"
}
}
Response Processingโ
{
"type": "pipeline_result",
"data": {
"data": {
"first_name": "John",
"last_name": "Doe",
"skills": [...],
"experiences": [...]
}
}
}
๐ซ What We Removedโ
Mock Upload APIโ
- Before: Custom
/api/uploadendpoint that returned fake MinIO URLs - After: Direct GraphQL + MinIO integration
Why the Mock Was Wrongโ
- Created fake MinIO URLs that don't exist
- Files weren't actually stored in MinIO
- Pipeline couldn't access the files
- Added unnecessary API layer
โ Current Implementationโ
The updated useCvExtractionWebSocket hook now:
- โ
Uses
GENERATE_UPLOAD_URLGraphQL mutation - โ Uploads directly to MinIO with signed URLs
- โ Sends real object paths via WebSocket
- โ Tracks upload progress properly
- โ Integrates with existing infrastructure
๐ Key Files Updatedโ
lib/hooks/useCvExtractionWebSocket.tsโ
- Added GraphQL mutation import
- Updated
uploadFilefunction to use proper MinIO flow - Sends
objectPathinstead of fake URLs
Removed Filesโ
app/api/upload/route.ts- No longer needed
๐งช Testingโ
To test the implementation:
- Upload a CV file - Should see real MinIO upload progress
- Check browser network tab - Should see PUT request to MinIO
- WebSocket message - Should contain real object path like
"cv-files/filename.pdf" - Pipeline processing - Should access file from MinIO successfully
๐ Production Notesโ
Environment Setupโ
- Ensure MinIO credentials are configured
- GraphQL server must have MinIO access
- Pipeline workers need MinIO read permissions
Securityโ
- Upload URLs are signed and time-limited
- Object paths are scoped to appropriate buckets
- No direct file access through web endpoints
This approach follows best practices for file handling in distributed systems and leverages your existing infrastructure properly.