dimah-s3v1.5.11

Routes

Named file routes — server-owned keys, constraints, and object policies.

A route is a named storage policy on your server. Each route owns its constraints, object keys, and enabled operations (upload, download, delete, and multipart).

The browser sends a route name with each request. The server enforces file constraints, validates permissions, and generates object keys.

lib/s3.ts
import { S3Client } from "@aws-sdk/client-s3";
import { dimahS3, route } from "@dimah-s3/server";

export const awsS3 = new S3Client({/* env */});

export const s3 = dimahS3({
  client: awsS3,
  bucket: process.env.S3_BUCKET!,
  routes: {
    avatar: route({
      upload: {
        fileTypes: ["image/*"],
        maxFileSize: 2 * 1024 * 1024,
      },
      download: true,
      delete: true,
    }),
    document: route({
      upload: {
        fileTypes: ["application/pdf", "image/*"],
        maxFileSize: 10 * 1024 * 1024,
      },
      download: true,
      delete: true,
    }),
    video: route({
      upload: {
        fileTypes: ["video/*"],
        maxFileSize: 500 * 1024 * 1024,
        multipart: true,
      },
    }),
  },
});

Combining operations

Enable operations on the same route when they manage the same objects. For example, if users upload, view, and remove their avatars:

avatar: route({
  upload: {
    fileTypes: ["image/*"],
    maxFileSize: 2 * 1024 * 1024,
  },
  download: true, 
  delete: true, 
}),

With the default keyPrefix (the route name), follow-up operations (confirm, download, and delete) must target keys within that route's namespace. That is route isolation, not per-user ownership — see Security.


Key generation and object identity

By default, the server generates object keys formatted as:

{keyPrefix}/{uuid}/{sanitizedFileName}

keyPrefix defaults to the route name (avatar → avatar/…, document → document/…). The same prefix is used twice:

  1. Upload / multipart init — the server generates the key. The client sends a route name, not an object key.
  2. Confirm, download, delete, multipart follow-ups — the client sends a key. The server accepts it only if it sits under that route's prefix.

So DELETE ?route=avatar&key=avatar/{uuid}/photo.jpg is valid, and DELETE ?route=avatar&key=document/{uuid}/cv.pdf returns INVALID_KEY. Two routes cannot share or nest prefixes (files and files/images); dimahS3() throws at startup.

Disabling namespace isolation

keyPrefix: false allows follow-up operations to target any safe key in the bucket. Use it only when strict route or operation guards enforce the complete access policy.

To customize key generation or organize files by user/tenant, return folder or key from upload.object:

avatar: route({
  upload: {
    fileTypes: ["image/*"],
    maxFileSize: 2 * 1024 * 1024,
    object: async ({ request }) => {
      const session = await getSession(request);
      return {
        folder: `users/${session.userId}`, 
        // Results in: avatar/users/{userId}/{uuid}/{fileName}
      };
    },
  },
}),

Type reference

import type {
  DimahS3RouteConfig,
  UploadConfig,
  DownloadConfig,
  DeleteConfig,
  MultipartConfig,
} from "@dimah-s3/server";

DimahS3RouteConfig

Prop

Type


UploadConfig

Prop

Type


DownloadConfig

Prop

Type


DeleteConfig

Prop

Type

Frequently asked questions

On this page