Installation
Install s3nd and give it credentials.
npm install s3ndpnpm add s3ndyarn add s3ndbun add s3ndThat is the server-side package. Depending on what you are building you may want one of the others instead, or as well:
| Package | Install it when |
|---|---|
s3nd | Your server talks to the bucket. Brings the AWS SDK. |
@s3nd/react | Your React app sends or receives transfers. Never pulls the SDK. |
@s3nd/cli | You want the s3nd command. Install it globally. |
@s3nd/protocol | You are writing a client or server for a runtime the others miss. |
@s3nd/react depends on @s3nd/protocol, never on s3nd, so a front-end
install does not drag a storage client into node_modules.
Node 20 or later — that is what AWS SDK v3 requires. @aws-sdk/client-s3 and
@aws-sdk/s3-request-presigner come along as dependencies, so there is nothing else to install
and no peer dependency to satisfy.
The package ships ESM and CommonJS builds with TypeScript declarations for both, so import and
require both work. It runs on your server only — there is no browser build, and no code path
that would put credentials in front of a user.
Credentials
s3nd never asks for credentials it does not need. Pass them explicitly:
import { createBucket } from 's3nd'
const store = createBucket({
bucket: 'my-bucket',
region: 'eu-west-3',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
})Or omit them and let the AWS default provider chain do its job — environment variables, the shared config file, an EC2 instance role, an ECS task role, a Lambda execution role:
const store = createBucket({ bucket: 'my-bucket', region: 'eu-west-3' })On a managed runtime, the role is the better answer: nothing to rotate and nothing to leak.
Environment variables
Every option except client falls back to an environment variable, so with S3_BUCKET and the
usual AWS_* variables set, this is enough:
const store = createBucket()See Configuration for the full list.
IAM permissions
The minimum policy for a bucket used by s3nd:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}getUrl() needs no extra permission: presigning is a local computation over the credentials you
already hold. The URL it produces grants exactly the s3:GetObject your own credentials have.
Snapshots need nothing beyond these three. putSnapshot() is a PutObject, getSnapshot() is a
GetObject, and the conditional writes ride along as headers on the same call.