Storage & Files

Manage file uploads, storage, and media processing with the SDK’s comprehensive storage operations. Handle images, videos, audio, and documents with ease.

⚠️ IMPORTANT: Storage operations return {path, name, mime, size, meta} structure. Use .path as the unique identifier (NOT .id or .url). Examples below use the correct .path and .mime fields.

File Uploads

Image Upload

Store user-uploaded images:

.input('profile_photo', 'file', { required: true })
 
.storageCreateImage(
  '$input.profile_photo',
  'public',
  'uploaded_image'
)
 
.dbEdit('users',
  { id: '$auth.id' },
  { 
    profile_photo_path: '$uploaded_image.path',  // ✅ Use .path
    profile_photo_name: '$uploaded_image.name'
  },
  'updated_user'
)
 
.response({
  image_path: '$uploaded_image.path',  // ✅ Correct field
  user: '$updated_user'
})

Access levels:

  • 'public' - Publicly accessible URLs
  • 'private' - Requires authentication to access

Video Upload

Handle video files with processing options:

.input('video', 'object', { required: true })
 
.createVideo(
  'private',
  '$input.video',
  'videos/$user.id_$timestamp.mp4',
  'uploaded_video',
  {
    encoding: 'h264',
    bitrate: '5000k',
    resolution: '1080p'
  }
)
 
.response({
  video_id: '$uploaded_video.id',
  url: '$uploaded_video.url'
})

Audio Upload

Store audio files:

.input('audio', 'object', { required: true })
 
.createAudio(
  'public',
  '$input.audio',
  'audio/$user.id_podcast.mp3',
  'uploaded_audio'
)
 
.response({
  audio_url: '$uploaded_audio.url',
  duration: '$uploaded_audio.duration'
})

Document Upload

Store documents and attachments:

.input('document', 'object', { required: true })
 
.createAttachment(
  'private',
  '$input.document',
  'documents/$user.id_$filename',
  'uploaded_doc'
)
 
.dbAdd('"documents"', {
  user_id: '$auth.id',
  file_id: '$uploaded_doc.id',
  filename: '$uploaded_doc.filename',
  size: '$uploaded_doc.size',
  uploaded_at: 'now'
}, 'doc_record')
 
.response({
  document: '$doc_record',
  file_url: '$uploaded_doc.url'
})

File Management

Download File from URL

Save external files to your storage:

.input('image_url', 'text', { required: true })
 
.storageCreateFromUrl(
  '$input.image_url',
  'imported/$timestamp.jpg',
  'downloaded_file'
)
 
.response({
  file_id: '$downloaded_file.id',
  local_url: '$downloaded_file.url'
})

Upload File to Path

Upload with specific path and metadata:

.input('file', 'object', { required: true })
 
.uploadFile(
  '$input.file',
  'uploads/$auth.id/$filename',
  'uploaded',
  {
    metadata: {
      uploaded_by: '$auth.id',
      category: '$input.category'
    }
  }
)
 
.response({
  file: '$uploaded'
})

Download File

Retrieve stored files:

.input('file_id', 'text', { required: true })
 
.downloadFile('$input.file_id', 'file_data')
 
.response({
  file: '$file_data',
  url: '$file_data.url',
  size: '$file_data.size'
})

Delete File

Remove files from storage:

.input('file_id', 'text', { required: true })
 
// Verify ownership
.dbGet('"documents"', { file_id: '$input.file_id' }, 'doc')
.precondition('$doc.user_id == $auth.id', 'Unauthorized')
 
.deleteFile('$input.file_id', 'deleted')
 
.dbDelete('"documents"', { file_id: '$input.file_id' }, 'deleted_record')
 
.response({
  deleted: true,
  file_id: '$input.file_id'
})

File Operations

List Files

Browse files in a directory:

.input('path', 'text', { default: 'uploads/' })
 
.listFiles('$input.path', 'files')
 
.response({
  files: '$files',
  count: 'count($files)'
})

Get File Metadata

Retrieve file information:

.input('file_id', 'text', { required: true })
 
.getFileMetadata('$input.file_id', 'metadata')
 
.response({
  filename: '$metadata.filename',
  size: '$metadata.size',
  type: '$metadata.type',
  created: '$metadata.created_at',
  modified: '$metadata.modified_at'
})

Move File

Relocate files in storage:

.input('file_id', 'text', { required: true })
.input('new_path', 'text', { required: true })
 
.moveFile(
  '$input.file_id',
  '$input.new_path',
  'moved_file'
)
 
.response({
  file: '$moved_file',
  new_path: '$input.new_path'
})

Copy File

Duplicate files in storage:

.input('file_id', 'text', { required: true })
.input('destination', 'text', { required: true })
 
.copyFile(
  '$input.file_id',
  '$input.destination',
  'copied_file'
)
 
.response({
  original: '$input.file_id',
  copy: '$copied_file'
})

Image Processing

Upload with Transformations

Process images during upload:

.input('image', 'object', { required: true })
 
.createImage(
  'public',
  '$input.image',
  'processed/$user.id.jpg',
  'original_image',
  {
    resize: {
      width: 800,
      height: 600,
      fit: 'cover'
    },
    quality: 85,
    format: 'jpg'
  }
)
 
.response({
  image_url: '$original_image.url',
  width: '$original_image.width',
  height: '$original_image.height'
})

Create Thumbnails

Generate image thumbnails:

.input('image', 'object', { required: true })
 
// Upload original
.createImage('public', '$input.image', 'originals/$user.id.jpg', 'original')
 
// Create thumbnail
.createImage(
  'public',
  '$original',
  'thumbnails/$user.id.jpg',
  'thumbnail',
  {
    resize: {
      width: 200,
      height: 200,
      fit: 'cover'
    }
  }
)
 
.response({
  original: '$original.url',
  thumbnail: '$thumbnail.url'
})

Complete Upload Flow

Profile Photo Upload

Complete example with validation and database storage:

const uploadProfilePhoto = XanoScript.create('users/profile-photo', 'POST')
  .description('Upload user profile photo')
  .requiresAuth('users')
  .input('photo', 'object', { required: true })
  
  // Validate file type
  .precondition(
    '$input.photo.type contains "image"',
    'File must be an image'
  )
  
  // Validate file size (5MB max)
  .precondition(
    '$input.photo.size <= 5242880',
    'File size must be under 5MB'
  )
  
  // Delete old photo if exists
  .dbGet('"users"', { id: '$auth.id' }, 'user')
  
  .conditional('$user.profile_photo_id != null')
    .then()
    .deleteFile('$user.profile_photo_id', 'deleted_old')
  .endConditional()
  
  // Upload new photo
  .createImage(
    'public',
    '$input.photo',
    'profiles/$auth.id.jpg',
    'new_photo',
    {
      resize: {
        width: 500,
        height: 500,
        fit: 'cover'
      },
      quality: 90
    }
  )
  
  // Create thumbnail
  .createImage(
    'public',
    '$new_photo',
    'profiles/thumbs/$auth.id.jpg',
    'thumbnail',
    {
      resize: {
        width: 100,
        height: 100,
        fit: 'cover'
      }
    }
  )
  
  // Update user record
  .dbEdit('"users"',
    { id: '$auth.id' },
    {
      profile_photo_id: '$new_photo.id',
      profile_photo_url: '$new_photo.url',
      thumbnail_url: '$thumbnail.url',
      updated_at: 'now'
    },
    'updated_user'
  )
  
  .response({
    photo_url: '$new_photo.url',
    thumbnail_url: '$thumbnail.url',
    user: {
      id: '$updated_user.id',
      email: '$updated_user.email',
      profile_photo_url: '$updated_user.profile_photo_url'
    }
  });

Document Management System

Upload and organize documents:

const uploadDocument = XanoScript.create('documents/upload', 'POST')
  .description('Upload document with metadata')
  .requiresAuth('users')
  .input('file', 'object', { required: true })
  .input('title', 'text', { required: true })
  .input('category', 'text')
  .input('tags', 'object')
  
  // Validate file
  .precondition(
    '$input.file.size <= 10485760',
    'File size must be under 10MB'
  )
  
  // Upload file
  .createAttachment(
    'private',
    '$input.file',
    'documents/$auth.id/$timestamp_$input.file.name',
    'uploaded_file'
  )
  
  // Create database record
  .dbAdd('"documents"', {
    user_id: '$auth.id',
    file_id: '$uploaded_file.id',
    filename: '$uploaded_file.filename',
    title: '$input.title',
    category: '$input.category',
    tags: '$input.tags',
    size: '$uploaded_file.size',
    type: '$uploaded_file.type',
    created_at: 'now'
  }, 'doc_record')
  
  .response({
    document: {
      id: '$doc_record.id',
      title: '$doc_record.title',
      filename: '$doc_record.filename',
      size: '$doc_record.size',
      created_at: '$doc_record.created_at'
    },
    file_url: '$uploaded_file.url'
  });

Batch Image Upload

Handle multiple file uploads:

const uploadGallery = XanoScript.create('gallery/upload', 'POST')
  .description('Upload multiple images to gallery')
  .requiresAuth('users')
  .input('images', 'object', { required: true })
  
  .var('uploaded_images', 'array', [])
  
  .forEach('$input.images', '$image')
    
    // Upload image
    .createImage(
      'public',
      '$image',
      'gallery/$auth.id/$timestamp_$image.name',
      'uploaded'
    )
    
    // Create thumbnail
    .createImage(
      'public',
      '$uploaded',
      'gallery/thumbs/$auth.id/$timestamp_$image.name',
      'thumb',
      {
        resize: { width: 300, height: 300, fit: 'cover' }
      }
    )
    
    // Save to database
    .dbAdd('"gallery_images"', {
      user_id: '$auth.id',
      file_id: '$uploaded.id',
      url: '$uploaded.url',
      thumbnail_url: '$thumb.url',
      created_at: 'now'
    }, 'image_record')
    
    .arrayPush('$uploaded_images', '$image_record')
    
  .endForEach()
  
  .response({
    images: '$uploaded_images',
    count: 'count($uploaded_images)'
  });

File Validation

Size Limits

Validate file sizes:

.input('file', 'object', { required: true })
 
.precondition(
  '$input.file.size <= 5242880',
  'File must be under 5MB'
)
 
.precondition(
  '$input.file.size > 0',
  'File is empty'
)

Common size limits:

  • 1MB: 1048576
  • 5MB: 5242880
  • 10MB: 10485760
  • 100MB: 104857600

File Type Validation

Restrict allowed file types:

.input('file', 'object', { required: true })
 
.conditional('$input.file.type contains "image"')
  .then()
  .createImage('public', '$input.file', 'images/$filename', 'uploaded')
.elseIf('$input.file.type contains "pdf"')
  .then()
  .createAttachment('private', '$input.file', 'pdfs/$filename', 'uploaded')
.else()
  .return({ error: 'Invalid file type' })
.endConditional()

Filename Sanitization

Clean filenames before storage:

.input('file', 'object', { required: true })
 
.filter('$input.file.name', ['trim', 'lower'], 'clean_name')
 
.var('safe_filename', 'text', 'replace($clean_name, " ", "_")')
 
.createAttachment(
  'private',
  '$input.file',
  'uploads/$auth.id/$timestamp_$safe_filename',
  'uploaded'
)

Best Practices

Organize by User

Keep user files organized:

'uploads/$auth.id/$category/$timestamp_$filename'
'images/users/$auth.id/$filename'
'documents/$auth.id/$year/$month/$filename'

Use Timestamps

Prevent filename collisions:

.var('timestamp', 'text', 'time()')
.createImage('public', '$file', 'uploads/$timestamp_$filename', 'uploaded')

Clean Up Old Files

Delete orphaned files:

.dbGet('"users"', { id: '$auth.id' }, 'user')
 
.conditional('$user.old_photo_id != null')
  .then()
  .deleteFile('$user.old_photo_id', 'deleted')
.endConditional()

Track File Usage

Maintain file metadata:

.dbAdd('"file_usage"', {
  user_id: '$auth.id',
  file_id: '$uploaded.id',
  context: 'profile_photo',
  created_at: 'now'
}, 'usage_record')

Secure Private Files

Verify access to private files:

.input('file_id', 'text', { required: true })
 
.dbGet('"documents"', { file_id: '$input.file_id' }, 'doc')
.precondition('$doc != null', 'File not found')
.precondition('$doc.user_id == $auth.id', 'Access denied')
 
.downloadFile('$input.file_id', 'file')

Next Steps