--- title: File API tags: [api, file, reference] --- # File API Endpoints live under `/api/files/*`. The router is [`backend/src/services/file/fileRoutes.ts`](../../backend/src/services/file/fileRoutes.ts), delegating to `fileController` and `fileService`. Multer is used for multipart parsing and uploaded files are written under `uploads/`. All endpoints require `Bearer JWT`. Static serving is wired in `app.ts` at `/uploads/*` with `helmet({ crossOriginResourcePolicy: { policy: "cross-origin" } })` so files can be embedded from the frontend domain. ## Multer configuration `fileService.getUploadMiddleware(options)` produces a Multer instance per route. Options: - `subfolder` - where the file lands under `uploads/` - `fieldName` - form field name - `fileTypes` - allowed MIME types (default: any) - `maxFiles` - default 1 - `maxFileSizeMB` - default 10 MB Global body limits in `app.ts` are `10mb` for `express.json` and `express.urlencoded`. ## Upload ### POST /api/files/upload/avatar **Description:** Upload an avatar image. Lands in `uploads/temp/` and is then moved when the user persists it on their profile. **Auth required:** Bearer JWT **Form fields:** `avatar` (image; JPEG / PNG / GIF / WebP) **Response 200:** ```json { "success": true, "data": { "url": "/uploads/temp/avatar-1716459200000.jpg", "filename": "avatar-1716459200000.jpg", "mimeType": "image/jpeg", "size": 51234 } } ``` **Errors:** `400` bad type / too large, `401` not authenticated. **Side effects:** None — caller is responsible for `PUT /api/user/profile` to persist the URL. ### POST /api/files/upload/file **Description:** Generic single-file upload (any MIME), lands in `uploads/temp/`. **Auth required:** Bearer JWT **Form fields:** `file` **Response 200:** `{ success, data: { url, filename, mimeType, size } }` ### POST /api/files/upload/files **Description:** Multi-file upload (up to 5). **Auth required:** Bearer JWT **Form fields:** `files` (repeated) **Response 200:** `{ success, data: { files: [{ url, filename, ... }] } }` ### POST /api/files/upload/request-template-images **Description:** Up to 10 images for a [[RequestTemplate]]. Lands in `uploads/request-templates/`. **Auth required:** Bearer JWT **Form fields:** `images` (repeated; JPEG / PNG / GIF / WebP) **Response 200:** `{ success, data: { files: [...] } }` ### POST /api/files/upload/blog-images **Description:** Up to 10 images for a [[BlogPost]]. Lands in `uploads/blog/`. **Auth required:** Bearer JWT **Form fields:** `images` (repeated; JPEG / PNG / GIF / WebP) **Response 200:** `{ success, data: { files: [...] } }` ## Delete ### DELETE /api/files/delete **Description:** Delete a file by relative path. **Auth required:** Bearer JWT **Request body:** `{ filePath: string }` (e.g. `"temp/avatar-1716459200000.jpg"`) **Response 200:** `{ success, message: "File deleted" }` **Errors:** `400` invalid path (must stay inside `uploads/`), `404` file missing. ## Inspect ### GET /api/files/info/:filePath **Description:** Returns metadata for a file (URL-encoded `filePath` segment). **Auth required:** Bearer JWT **Response 200:** `{ success, data: { url, size, mimeType, createdAt } }` ### GET /api/files/stats **Description:** Aggregate upload statistics (counts and sizes per subfolder). **Auth required:** Bearer JWT (admin gating planned per TODO in source) **Response 200:** `{ success, data: { subfolders: [{ name, count, sizeBytes }], total: { count, sizeBytes } } }` ## Serving - `GET /uploads/` - static file delivery (no auth, public read). Suitable for embedding avatars and blog/template images. - The server logs `❌ File not found:` for missing paths and `✅ Serving file:` on hits — useful when debugging frontend image refs. The on-disk root resolves from `config.uploadPath`. In production this defaults to `/app/uploads`; in development to `/uploads`. Both are normalised to an absolute path before being passed to `express.static`. ## Related - [[File Storage Architecture]] - [[User API]] (avatar consumer) - [[Marketplace API]] (template image consumer) - [[Blog API]] (blog image consumer) - [[Chat API]] (message attachments use a separate multipart route under chat)