Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

08 — Form Specifications (Files Module)

Files module has exactly one form: the upload sheet. Everything else is pick→confirm→list. Shared form/validation conventions: 00-shared/03 (components), 00-shared/07_API_Conventions (envelope + errors).


1. Upload Form (multipart)

1.1 Contract

AspectValueSource
EndpointPOST /api/v1/files/uploadfiles.controller.ts:29-41
Content-Typemultipart/form-dataApiConsumes, files.controller.ts:32
Field namefile (single, binary)FileInterceptor('file'), files.controller.ts:31
Success201 + FileRecordservice returns persisted doc, files.service.ts:45
Authfile.uploadfiles.controller.ts:30

1.2 Fields

FieldTypeRequiredServer validationNotes
file (binary)Bufferyesnone (gap)client supplies originalname, mimetype, size; server stores verbatim (files.service.ts:32-33)

There is no DTOdto/upload-file.dto.ts does not exist in the module (only controller/service/module/schema/repository). Server accepts anything Multer parses.

1.3 Client-side validation matrix

RuleClient behaviorServer today
File selectedblock Upload until selected
Size 0 bytesblock: "Empty file"accepts (gap)
Size > 25 MBwarn + confirm (soft cap) (proposed)accepts (no limit anywhere — gap; consider multer limits.fileSize (planned))
MIME allowlistoptional gating on common types (proposed)accepts any mimetype string (spoofable — QA item, 14_QA_Checklist.md)
Filename sanitystrip control chars client-side onlystored raw into key ${uuid}--${originalName} (files.service.ts:31) and into Content-Disposition header (files.controller.ts:61)

1.4 Error mapping

HTTPMeaningUI
400malformed multipart (missing file part)sheet error "Choose a file"
401 / 403unauthenticated / no file.uploadauth flow / permission state (guards (planned))
413payload too large — when a multer limit lands (planned)size copy + suggestion
500storage provider failure (storage.upload throws, files.service.ts:29-34)"Upload failed — retry" (no server idempotency; retry = new record)
offlinetransport errorAppOfflineBanner + retry queue (forward-looking)

1.5 Flow states

idle → selected → submitting (progress %; cancel) → success | error → retry
  • Success: sheet closes, tile inserts optimistically into list (rollback on fail).
  • Cancel: abort client request only; server discards partial body (no partial cleanup job yet — STORAGE_ARCHITECTURE.md:66 (planned)).

1.6 A11y

  • Live region announces progress; cancel reachable via keyboard; error text announced; focus returns to Upload on failure.

2. Future forms (planned)

  • Signed-URL request: GET /files/:id/signed-url?expiresInSeconds=3600 → provider getSignedUrl(fileId, expiresInSeconds) (storage-provider.ts:25) — R2 presigns (r2.provider.ts:77-86); Appwrite currently ignores the TTL (appwrite-storage.provider.ts:69-74); local returns a fake path (local-storage.provider.ts:47-51). Not wired into the service yet.
  • Caption/metadata form: no file.update permission exists (permissions.constants.ts:86-88) — skip until a module needs it.