The short answer
To add file uploads, tell your connected coding agent to create a bucket and save uploads to it. Antideploy creates a private S3 bucket for your app, writes its variables into your .env without printing the key, and gives the running app the same variables. Any S3 client works with it. Never save uploads to the app's own disk, because the disk is replaced on every deploy.
Say this to your coding agent
Add file uploads to this project and store the files in a bucket.
If your agent is not connected yet, paste this first: Set this project up to deploy on Antideploy. Fetch https://antideploy.com/agent.md and follow it.
Why not the disk
A container is replaced on every deploy and its disk goes with it. A user's avatar saved with multer or fs.writeFile is gone after the next deploy, and nothing will error. Antideploy flags code that saves uploads to disk before it builds anything. A bucket is not replaced, and it belongs to your application.
What your agent does
-
It creates the bucket before it writes the upload code
So the upload is written and tested against the real thing. Asking twice returns the same bucket.
-
It writes the variables into your
.envThe key goes into a file through a redirect that never prints it. The deploy that follows gives the running app the same variables.
-
Your code uses any S3 client
The AWS SDKs and boto3 read the
AWS_*variables from the environment, so code that reads them and sets nothing else already works.
A project that uses an S3 client and brought no key of its own gets a bucket at its first deploy, even without asking.
Upload straight from the browser
Have your server return a presigned PUT URL. The browser sends the file straight to the bucket, without passing through your app. Browsers on any origin may call the bucket, and every request still needs the key or a link signed with it.
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({ forcePathStyle: true });
const url = await getSignedUrl(
s3,
new PutObjectCommand({ Bucket: process.env.BUCKET_NAME, Key: "avatars/maya.webp" }),
{ expiresIn: 300 },
);
// hand url to the browser, which PUTs the file straight to the bucket
# Python
import os, boto3
s3 = boto3.client("s3")
s3.upload_file("avatar.webp", os.environ["BUCKET_NAME"], "avatars/maya.webp")
Showing a file to a visitor
The bucket is private, so there is no public URL. To show a file, serve it through your app, or hand the visitor a signed link that expires.
What you get
- One private bucket per app, on Neon Object Storage in Singapore
- A key that reaches that bucket and nothing else
- Storage is not metered or capped today
- Buckets per account: 1 on Free, 2 on Go, 3 on Pro and 5 on Scale