S3-compatible file storage patterns -- uploads, pre-signed URLs, CDN, multipart. Use when project has @aws-sdk/client-s3, aws-sdk, or minio in package.json.
Detection: Check package.json for @aws-sdk/client-s3, aws-sdk, or minio. Also check for S3-compatible services: Cloudflare R2, DigitalOcean Spaces, MinIO. If absent, skip.
import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3 = new S3Client({
region: process.env.S3_REGION,
endpoint: process.env.S3_ENDPOINT, // for R2/MinIO/Spaces
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY!,
secretAccessKey: process.env.S3_SECRET_KEY!,
},
});
// Server: generate pre-signed upload URL
async function getUploadUrl(filename: string, contentType: string) {
const key = `uploads/${crypto.randomUUID()}/${filename}`;
const url = await getSignedUrl(s3, new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: key,
ContentType: contentType,
}), { expiresIn: 3600 }); // 1 hour
return { url, key };
}
// Client: upload directly to S3
const { url, key } = await api.getUploadUrl(file.name, file.type);
await fetch(url, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } });
// Save `key` in your database
await s3.send(new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: `avatars/${userId}.jpg`,
Body: buffer,
ContentType: 'image/jpeg',
}));
// Pre-signed download URL (private files)
const url = await getSignedUrl(s3, new GetObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: fileKey,
}), { expiresIn: 3600 });
// Public URL via CDN (public files)
const publicUrl = `${process.env.CDN_URL}/${fileKey}`;
uploads/{uuid}/{filename}