Uploadthing
UploadThing
Just need ad-hoc uploads? Wrap this adapter in the
Filesfacade for a one-liner API. The reference below shows direct adapter usage and UploadThing-specific key handling.
Overview
The UploadThing adapter wraps UTApi from uploadthing/server. Uploads are keyed by user-supplied customId so subsequent operations (delete, signed URL, list) round-trip on the user's key rather than UploadThing's internal fileKey. Read URLs are minted as public CDN URLs (<appId>.ufs.sh/f/<customId>) when the ACL is public-read, or as signed URLs via generateSignedURL() when the ACL is private.
Installation
npm install uploadthingyarn add uploadthingpnpm add uploadthingUsage
import UploadThingStorage from "@visulima/storage/provider/uploadthing";
const storage = new UploadThingStorage({
token: process.env.UPLOADTHING_TOKEN!,
acl: "public-read",
});The token is the base64-encoded JSON from your UploadThing dashboard — it embeds both apiKey and appId. The adapter decodes it once at construction to extract appId for CDN URL minting.
Configuration
Pre-built client
import { UTApi } from "uploadthing/server";
const storage = new UploadThingStorage({
client: new UTApi({ defaultKeyType: "customId", token: process.env.UPLOADTHING_TOKEN! }),
token: process.env.UPLOADTHING_TOKEN!, // still required — used to derive appId
});Even with a pre-built client, token is required so the adapter can extract appId for CDN URLs.
ACL
new UploadThingStorage({
token: process.env.UPLOADTHING_TOKEN!,
acl: "private", // or "public-read" (default)
});public-read: every upload is publicly readable;getReadUrl()returns the CDN URL synchronously without a round-trip.private:getReadUrl()callsutapi.generateSignedURL()for each request.
Default signed URL lifetime
new UploadThingStorage({
/* … */
defaultUrlExpiresIn: 3600, // seconds; capped at 7 days (604_800)
});URL Generation
getReadUrl(key, options?)
public-readACL: returnshttps://<appId>.ufs.sh/f/<encoded-customId>synchronously.privateACL: returns a signed URL viagenerateSignedURL(key, { expiresIn, keyType: "customId" }).expiresInis clamped to 7 days.responseContentDispositionis not supported.
getUploadUrl(key)
Throws METHOD_NOT_ALLOWED. UploadThing's signed PUT URLs require HMAC signing against a UFS ingest endpoint — out of scope for this adapter. Callers that need direct-from-browser uploads should use UploadThing's file-router pattern via storage.raw.
Features
- customId keying — operations are addressed by your virtual key, not UploadThing's internal fileKey.
- ACL-aware reads — public files use the CDN directly; private files use signed URLs only when needed.
- Token-derived
appId— no separate config field; the dashboard token already contains everything. - Native
listFiles—storage.list()round-trips a singleutapi.listFiles({ limit }).
Limitations
- No native presigned upload URLs — use the file-router pattern via
storage.rawfor direct-from-browser uploads. copy()is download-then-reupload (UploadThing has no server-side copy).list()has no server-side prefix filter — prefix filtering happens client-side per page.- Signed URLs cap at 7 days.