Shop It Docs
Developer ResourcesUploads & Storage

API Reference

All upload and storage endpoints — admin and mobile — with request/response shapes.

Upload API Reference

Audience: backend, frontend Scope: apps/api/src/modules/upload/upload.controller.ts, mobile-upload.controller.ts

All endpoints require Authorization: Bearer <token>.


Admin Endpoints — /upload

POST /upload

Upload a single file. No file-type restriction. Size limit is controlled by UPLOAD_MAX_FILE_SIZE_MB (default 50 MB).

Query params

ParamTypeDefaultDescription
uploadTypeUploadType enumimageDestination folder and access visibility
optimizebooleantrueRun image optimisation (Sharp → WebP)

Bodymultipart/form-data

FieldRequired
file

Response 200

{
  "message": "File uploaded successfully",
  "data": {
    "filename": "sdcupH633744750-photo.webp",
    "originalName": "photo.jpg",
    "mimeType": "image/webp",
    "size": 48320,
    "relativePath": "uploads/image/sdcupH633744750-photo.webp",
    "url": "http://minio:9000/apple/uploads/image/sdcupH633744750-photo.webp?X-Amz-...",
    "expiresAt": 1712250000000
  }
}

For public upload types (avatar, landing, thumbnail):

{
  "data": {
    "relativePath": "public/avatar/sdcupH633744750-photo.webp",
    "url": "http://minio:9000/apple/public/avatar/sdcupH633744750-photo.webp",
    "expiresAt": null
  }
}

Store relativePath in the DB, never url. Signed URLs expire after ~2 hours. Public URLs are permanent but the bucket endpoint may change.


POST /upload/multiple

Upload up to 10 files in one request.

Query params — same as single upload.

Bodymultipart/form-data

FieldRequiredNote
files1–10 files

Response 200 — array of UploadResultDto (same shape as single upload).


GET /upload/signed-url/*key

Resolve a stored relativePath to a viewable URL. Use this when a previously issued signed URL has expired and the frontend needs a fresh one.

Path paramkey: the relativePath value from the DB (supports slashes, e.g. uploads/image/abc.webp).

Query params

ParamTypeDescription
expiresInnumber (seconds)Override the default signed URL TTL

Response 200

{
  "message": "Signed URL generated",
  "data": {
    "url": "http://minio:9000/apple/uploads/image/abc.webp?X-Amz-...",
    "expiresAt": 1712250000000
  }
}

For public keys (public/*):

{
  "message": "Local URL resolved",
  "data": {
    "url": "http://minio:9000/apple/public/avatar/abc.webp",
    "expiresAt": null
  }
}

POST /upload/signed-urls

Batch URL resolution — more efficient than calling GET /upload/signed-url once per file.

Body application/json

{
  "keys": [
    "uploads/image/abc.webp",
    "public/avatar/xyz.webp",
    "uploads/file/report.pdf"
  ],
  "expiresIn": 3600
}

Response 200

{
  "message": "URLs resolved",
  "data": {
    "uploads/image/abc.webp": {
      "url": "http://minio:9000/apple/uploads/image/abc.webp?X-Amz-...",
      "expiresAt": 1712250000000
    },
    "public/avatar/xyz.webp": {
      "url": "http://minio:9000/apple/public/avatar/xyz.webp",
      "expiresAt": null
    },
    "uploads/file/report.pdf": {
      "url": "http://minio:9000/apple/uploads/file/report.pdf?X-Amz-...",
      "expiresAt": 1712250000000
    }
  }
}

GET /upload/file/*key

Backend proxy — streams a file through the NestJS server. Use this when you do not want to expose MinIO URLs to the client at all.

Trade-off: every request goes through NestJS, adding latency and server load. Suitable for low-traffic or security-sensitive files.

Response — raw file stream with Cache-Control: private, max-age=3600.


Mobile Endpoints — /mobile/uploads

All mobile endpoints are rate-limited per IP:

EndpointLimit
POST /mobile/uploads10 requests / 60 s
GET /mobile/uploads/signed-url/*key60 requests / 60 s

POST /mobile/uploads

Single file upload for authenticated customers. Fixed 5 MB size limit (not configurable per-env).

Query params

ParamTypeDefaultDescription
uploadTypeCustomerUploadTypeimageavatar, image, or file only
optimizebooleantrueImage optimisation

landing, thumbnail, and video are not available on this endpoint. Attempts to pass them will fail class-validator with a 400.

Bodymultipart/form-data, field name file.

Response — same UploadResultDto shape as admin upload.


GET /mobile/uploads/signed-url/*key

Identical logic to GET /upload/signed-url/*key but rate-limited for customer usage.


Upload Flow — Sequence


Error Responses

StatusCondition
400No file provided, or uploadType fails enum validation
413File exceeds size limit (Multer or MinIO)
502Remote storage rejected the upload (auth error, quota, etc.)
429Rate limit exceeded (mobile endpoints only)