Typed assets
Represent avatars, documents, banners, and other uploads as named, strongly typed configurations.
Define typed assets, validate file information, and prepare secure upload targets for AWS S3 and ImageKit through one focused API.
Browser
Sends filename, content type, and file size
Application server
Authenticates the request and calls the SDK
Upload SDK
Validates, resolves the profile, and signs
Storage provider
Receives the file directly from the browser
Method
POST
Body
FormData
Strategy
multipart
2
Providers
AWS S3 and ImageKit
1
Upload strategy
Multipart POST
0
Client secrets
Credentials remain server-side
100%
Typed configuration
Assets and profiles are inferred
Why Upload SDK
Your application describes upload intent. The SDK handles shared validation and provider-specific signing.
Represent avatars, documents, banners, and other uploads as named, strongly typed configurations.
Route assets through named AWS S3 or ImageKit profiles without leaking provider details into product code.
Validate filename, key prefix, MIME type, extension, size, and expiration before generating upload credentials.
Keep provider credentials inside server-side factories and return only short-lived upload fields.
How it works
The server prepares the upload. The browser transfers the actual file directly to the provider.
Create named storage profiles using AWS S3 or ImageKit credentials.
Describe file rules, limits, metadata, storage profile, and key prefix.
Send file metadata to your server and call prepareUpload.
Submit the returned fields and file directly to the provider.
Developer experience
Profiles contain provider configuration. Assets describe what your application accepts. The uploader connects them at runtime.
import {
awsS3,
createUploader,
defineAssets,
defineStorageProfiles,
} from "@marinedotsh/upload-sdk";
const storageProfiles = defineStorageProfiles({
media: awsS3({
bucket: process.env.AWS_BUCKET!,
region: process.env.AWS_REGION!,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
}),
});
const assets = defineAssets(storageProfiles, {
avatar: {
storageProfile: "media",
keyPrefix: "uploads/avatars",
expiresIn: { value: 5, unit: "minutes" },
accept: {
mimeTypes: ["image/png", "image/jpeg"],
extensions: [".png", ".jpg", ".jpeg"],
},
limits: {
maxFileSize: { value: 1, unit: "MB" },
},
},
});
export const uploader = createUploader({
storageProfiles,
defaultStorageProfile: "media",
assets,
});Asset
avatar
Profile
media
Provider
AWS S3
const prepared = await uploader.prepareUpload("avatar", {
filename: "profile.png",
contentType: "image/png",
size: 120_000,
});
const formData = new FormData();
for (const [name, value] of Object.entries(prepared.fields)) {
formData.append(name, value);
}
formData.append("file", file);
await fetch(prepared.url, {
method: prepared.method,
headers: prepared.headers,
body: formData,
});Prepared result
Providers
The application-facing preparation flow remains stable while each provider generates the fields it requires.
Signature V4 POST policy
Generate signed multipart POST fields with constrained object keys, content type, metadata, expiration, and optional file-size limits.
V2 upload JWT
Generate short-lived upload JWTs with destination fields, overwrite protection, metadata, and optional MIME checks.
Clear boundaries
Keeping these responsibilities separate prevents the SDK from becoming tightly coupled to your framework, database, or authentication system.
Install the SDK, configure a storage profile, and define your first typed upload asset.
$pnpm add @marinedotsh/upload-sdk