dimah-s3v1.5.11

Server Setup

Install `@dimah-s3/server`, configure S3, and mount your route handler.

Install

npm i @dimah-s3/server @aws-sdk/client-s3

Configure S3 and 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.

lib/s3.ts
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,
    }),
  },
});

Uploading more than 5 GiB

A single presigned POST cannot exceed 5 GiB. For larger files, enable upload.multipart or set upload.method to "PUT" when your storage provider supports a single PUT of that size. The route's maxFileSize still applies.


Mount route handler

Adapters are available for all major JavaScript runtimes:

app/api/s3/[...s3]/route.ts
import { toNextJsHandler } from "@dimah-s3/server/next";
import { s3 } from "@/lib/s3";

export const { GET, POST, PUT, PATCH, DELETE } = toNextJsHandler(s3);

Cache Components

Next.js 16.4 enables Cache Components on new apps. This handler reads the request, so presign, download, and delete run on each request. Leave the handler uncached. A use cache wrapper would reuse one presign across requests.


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:

app/actions.ts
"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(), 
  });
}

Guards and resolveScope see that call as a Request: headers are copied, query is on the URL, and a JSON body stays readable with request.clone().json(). Pass request only when you already have the incoming one — that object is used as-is.

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 } from "@dimah-s3/server";

DimahS3Config

Prop

Type

Frequently asked questions

On this page