Setup
Install `@dimah-s3/server`, configure S3, and mount your route handler.
Install
npm i @dimah-s3/server @aws-sdk/client-s3Configure S3 & routes
Initialize dimahS3 by passing your @aws-sdk/client-s3 instance and defining named routes.
Operations (upload, download, delete, multipart) are disabled by default. Enable only what each route needs.
import { S3Client } from "@aws-sdk/client-s3";
import { dimahS3, route } from "@dimah-s3/server";
export const awsS3 = new S3Client({
region: process.env.S3_REGION,
endpoint: process.env.S3_ENDPOINT,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID!,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
},
});
export const s3 = dimahS3({
client: awsS3,
bucket: process.env.S3_BUCKET!,
routes: {
avatar: route({
upload: {
fileTypes: ["image/*"],
maxFileSize: 2 * 1024 * 1024, // 2MB
},
download: true,
delete: true,
}),
},
});Mount route handler
Adapters are available for all major JavaScript runtimes:
import { toNextJsHandler } from "@dimah-s3/server/next";
import { s3 } from "@/lib/s3";
export const { GET, POST, PUT, PATCH, DELETE } = toNextJsHandler(s3);Server-side API (s3.api)
You can invoke S3 actions directly on the server without going through HTTP endpoints. Pass request headers so that route guards and auth hooks execute properly:
"use server";
import { headers } from "next/headers";
import { s3 } from "@/lib/s3";
export async function getAvatarDownloadUrl(key: string) {
return s3.api.download({
query: { route: "avatar", key },
headers: await headers(),
});
}To upload files directly from the server (e.g. background jobs, image processing) while still executing route validation and onConfirmed hooks:
await s3.put({
route: "avatar",
fileName: "profile.png",
contentType: "image/png",
body: imageBuffer,
headers: await headers(),
});Type reference
import type { DimahS3Config, DimahS3Logger } from "@dimah-s3/server";DimahS3Config
Prop
Type
Frequently asked questions
All handlers throw typed APIError objects with stable string codes and numeric HTTP status codes:
- A plain
Errorthrown inguard,*Guard, orobjectmaps to 403 Forbidden. - A plain
Errorthrown inonPresigned,onConfirmed, or otheron*hooks maps to 500 Internal Error. - Use the
errorshelper (e.g.throw errors.unauthorized()) to specify exact codes.
See Errors for the full list of error codes.
By default, dimah-s3 isolates object keys by route prefix:
avatar/{uuid}/{fileName}All follow-up operations (confirm, download, delete) must target keys within the route's keyPrefix. Any request targeting a key outside its prefix is rejected with INVALID_KEY.
To customize the folder or key structure, use upload.object in your route definition.
Set basePath when initializing dimahS3:
export const s3 = dimahS3({
basePath: "/api/storage",
// ...
});Make sure the client specifies the same basePath (or full baseURL if hosted on a separate domain):
export const s3Client = createS3Client({
baseURL: "https://api.example.com/api/storage",
});