Skip to content

S3 Service API

File Information

  • Path: api/s3.js
  • Base Path: /s3
  • Framework: Express.js
  • AWS SDK: @aws-sdk/client-s3 v3

Purpose

The S3 service provides server-side endpoints for AWS S3 operations, including multipart uploads, file downloads, and bucket configuration management.

Dependencies

  • @aws-sdk/client-s3 - AWS S3 client
  • @aws-sdk/s3-request-presigner - Presigned URL generation
  • express - Web framework
  • multer - File upload handling
  • cors - CORS middleware

Endpoints

GET /s3/getBucketConfig

Get S3 bucket CORS configuration.

Response:

json
{
  "CORSRules": [...]
}

POST /s3/updateBucketConfig

Update S3 bucket CORS configuration for chunked uploads.

Request Body: None (uses default CORS rules)

Response:

json
{
  "success": true
}

GET /s3/start-upload

Initiate a multipart upload session.

Query Parameters:

  • workspaceId (required) - Workspace identifier
  • fileType (required) - MIME type of the file

Response:

json
{
  "uploadId": "multipart-upload-id",
  "assetId": "unique-asset-id"
}

Example:

javascript
GET /s3/start-upload?workspaceId=123&fileType=image/jpeg

GET /s3/get-upload-url

Get presigned URL for uploading a chunk.

Query Parameters:

  • PartNumber (required) - Part number (1-indexed)
  • UploadId (required) - Multipart upload ID
  • assetId (required) - Asset identifier
  • workspaceId (required) - Workspace identifier

Response:

json
{
  "url": "presigned-upload-url",
  "expiresIn": 3600
}

Example:

javascript
GET /s3/get-upload-url?PartNumber=1&UploadId=xxx&assetId=yyy&workspaceId=123

POST /s3/complete-upload

Complete multipart upload and merge all parts.

Request Body:

json
{
  "uploadId": "multipart-upload-id",
  "assetId": "asset-id",
  "workspaceId": "workspace-id",
  "parts": [
    {
      "ETag": "part-etag",
      "PartNumber": 1
    }
  ]
}

Response:

json
{
  "success": true,
  "location": "s3://bucket/key",
  "key": "file-key"
}

GET /s3/download

Download file from S3.

Query Parameters:

  • key (required) - S3 object key
  • workspaceId (required) - Workspace identifier

Response: File stream with appropriate headers

Example:

javascript
GET /s3/download?key=workspace/digital_assets/file.jpg&workspaceId=123

DELETE /s3/delete

Delete file from S3.

Query Parameters:

  • key (required) - S3 object key
  • workspaceId (required) - Workspace identifier

Response:

json
{
  "success": true
}

Multipart Upload Flow

  1. Start Upload: Client calls /s3/start-upload to get uploadId and assetId
  2. Upload Chunks: For each chunk:
    • Get presigned URL from /s3/get-upload-url
    • Upload chunk directly to S3 using presigned URL
    • Store ETag and PartNumber
  3. Complete Upload: Call /s3/complete-upload with all part ETags

S3 Configuration

Bucket Structure

{workspaceId}/
  digital_assets/
    {assetId}

CORS Rules

javascript
{
  AllowedMethods: ['GET', 'POST', 'PUT', 'DELETE'],
  AllowedOrigins: ['*'],
  AllowedHeaders: ['*'],
  ExposeHeaders: ['ETAG']
}

Error Handling

All endpoints return consistent error responses:

json
{
  "error": "Error message",
  "code": "ERROR_CODE"
}

Common error codes:

  • MISSING_PARAMETER - Required parameter missing
  • UPLOAD_FAILED - Upload operation failed
  • INVALID_UPLOAD_ID - Invalid upload ID
  • S3_ERROR - AWS S3 error

Security

  • All operations require workspace validation
  • Presigned URLs expire after 1 hour
  • File keys are workspace-scoped
  • CORS is configured for cross-origin requests

Environment Variables

  • AWS_ACCESS_KEY_ID - AWS access key
  • AWS_SECRET_ACCESS_KEY - AWS secret key
  • AWS_BUCKET - S3 bucket name
  • AWS_DEFAULT_REGION - AWS region

Usage Example

javascript
// Start multipart upload
const { uploadId, assetId } = await axios.get('/s3/start-upload', {
  params: { workspaceId: '123', fileType: 'image/jpeg' }
})

// Upload chunks
const parts = []
for (let i = 0; i < chunks.length; i++) {
  const { url } = await axios.get('/s3/get-upload-url', {
    params: {
      PartNumber: i + 1,
      UploadId: uploadId,
      assetId: assetId,
      workspaceId: '123'
    }
  })
  
  const etag = await uploadChunkToS3(url, chunks[i])
  parts.push({ ETag: etag, PartNumber: i + 1 })
}

// Complete upload
await axios.post('/s3/complete-upload', {
  uploadId,
  assetId,
  workspaceId: '123',
  parts
})