Skip to main content
Live. This area is documented as current, user-reliable behavior.

Goal

Upload a public asset and get a CDN URL with the official SDK.

Prerequisites

  • A StackShift API key
  • An SDK installed in your backend app

Workflow

1
Create a StackShift client with apiKey.
2
Call the Assets upload method with a file, bucket, key, visibility, and metadata.
3
Store the returned asset ID in your app if you need later replacement or deletion.
4
Use asset.url for originals, assets.url for transforms, videoUrl helpers for playback outputs, and version helpers for pinned delivery.

TypeScript

import { StackShift } from '@stackshift-cloud/sdk'

const stackshift = new StackShift({
  apiKey: process.env.STACKSHIFT_API_KEY!,
})

const asset = await stackshift.assets.upload(file, {
  bucket: 'avatars',
  key: 'users/user_123.png',
  visibility: 'public',
  metadata: { userId: 'user_123' },
})

console.log(asset.url)

NestJS

Wrap the SDK in a service and accept files through NestJS Multer interceptors. For larger browser uploads, create signed upload URLs instead of buffering through the NestJS process.
import { Injectable } from '@nestjs/common'
import { StackShift } from '@stackshift-cloud/sdk'

@Injectable()
export class AssetsService {
  private readonly stackshift = new StackShift({
    apiKey: process.env.STACKSHIFT_API_KEY!,
  })

  upload(file: Express.Multer.File) {
    return this.stackshift.assets.upload(
      new Blob([file.buffer], { type: file.mimetype }),
      {
        bucket: 'uploads',
        key: file.originalname,
        visibility: 'private',
        metadata: { originalName: file.originalname },
      },
    )
  }
}
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common'
import { FileInterceptor } from '@nestjs/platform-express'
import { AssetsService } from './assets.service'

@Controller('assets')
export class AssetsController {
  constructor(private readonly assets: AssetsService) {}

  @Post('upload')
  @UseInterceptors(FileInterceptor('file'))
  upload(@UploadedFile() file: Express.Multer.File) {
    return this.assets.upload(file)
  }
}

React, Next.js, and TanStack Start

Browser apps should never receive the StackShift API key. Create a signed upload URL on a server route, then upload the browser File directly to StackShift Assets.
Server route
import { StackShift } from '@stackshift-cloud/sdk'

const stackshift = new StackShift({
  apiKey: process.env.STACKSHIFT_API_KEY!,
})

export async function createAvatarUpload() {
  return stackshift.assets.signedUploadUrl({
    bucket: 'avatars',
    key: `users/${crypto.randomUUID()}.png`,
    visibility: 'public',
    expiresIn: '10m',
    maxBytes: 5_000_000,
  })
}
Browser client
async function uploadAvatar(file: File) {
  const upload = await fetch('/api/assets/avatar-upload', {
    method: 'POST',
  }).then((response) => response.json())

  await fetch(upload.url, {
    method: upload.method,
    body: file,
    headers: { 'Content-Type': file.type },
  })
}

Python

from stackshift import StackShift

stackshift = StackShift(api_key="sk_live_...")

asset = stackshift.assets.upload(
    "avatar.png",
    bucket="avatars",
    key="users/user_123.png",
    visibility="public",
    metadata={"user_id": "user_123"},
)

print(asset["url"])

Go

import (
    "context"
    "log"
    "os"

    stackshift "github.com/stackshiftCloud/assets-go"
)

client, err := stackshift.New(stackshift.Options{
    APIKey: os.Getenv("STACKSHIFT_API_KEY"),
})

file, _ := os.Open("avatar.png")
defer file.Close()

asset, err := client.Assets.Upload(ctx, stackshift.AssetUpload{
    File: file,
    FileName: "avatar.png",
    Bucket: "avatars",
    Key: "users/user_123.png",
    Visibility: "public",
})

log.Println(asset.URL)

Expected result

Your app uploads a file to StackShift Assets and receives provider-neutral asset metadata.

StackShift Assets overview

StackShift Assets is now a live media platform: storage, CDN delivery, image optimization, upload sessions, DAM, video, scanning, governance, AI metadata, and version history.

Direct browser uploads

Create a short-lived signed upload URL on your server, then PUT the file directly from the browser.

Image optimization

Use named presets and signed dynamic transforms for strict, cached, responsive image delivery.

AI DAM and versioning

Use OpenAI-backed asset intelligence, moderation, transcripts, smart crops, background removal, collections, saved searches, and branching versions.