Skip to content

Uploads Overview

Laju Go provides two upload mechanisms, each for a different use case. Picking the right one is a matter of file size and whether you need the result back immediately.

Multipart FormData

One POST request, CSRF protected, response includes the file URL. Best for small files (< 5MB) where you need to update the database immediately — like avatars and profile photos.

TUS resumable uploads

Chunked, resumable, handles network failures. Best for large files (≥ 100MB, no hard limit) where the upload might fail mid-way — like videos and archives.

Your file is… Use Why
An avatar, profile photo, or any image < 5MB Multipart One POST, immediate DB update, CSRF protected
A video, archive, or any file ≥ 100MB TUS Chunked, resumable, handles network failures
A file that needs to update a DB field after upload Multipart Response includes URL, persist via form.put()
A file that just needs to be stored and downloaded later TUS No DB update needed, async post-processing

Rule of thumb: if the file fits in a single POST and you need the URL back immediately, use multipart. If the file is ≥ 100MB or the upload might fail mid-way, use TUS.

Factor Multipart TUS
File size < 5MB ≥ 100MB (no hard limit)
Resumable No Yes
DB update needed Yes (avatar URL) No
CSRF Yes No (TUS has its own protocol)
Requests per upload 1 (POST) 3+ (POST, HEAD, PATCH…)
Endpoint POST /app/upload POST /tus/files
Auth Session + CSRF Session only

Both mechanisms write to storage/, but in different subdirectories:

storage/
├── avatars/ ← multipart uploads (avatars)
│ └── <userID>_<timestamp>.<ext>
├── uploads/ ← TUS internal format (by upload ID)
│ ├── <upload-id>
│ └── <upload-id>.info
└── completed/ ← TUS post-processed (original filenames)
└── <original-name>

All files are served publicly via app.Static("/storage", "./storage").