Upload Hooks
Presign, authorize, confirm, and persist verified upload metadata.
Enable upload operations on a named route with upload: true or an UploadConfig object.
import { S3Client } from "@aws-sdk/client-s3";
import { dimahS3, errors, 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,
guard: async ({ request }) => {
const session = await getSession(request);
if (!session) throw errors.unauthorized();
},
onConfirmed: async ({ key, contentLength, request }) => {
const session = await getSession(request);
await db.user.update({
where: { id: session.userId },
data: { avatarKey: key, avatarSize: contentLength },
});
},
},
}),
},
});Upload lifecycle flow
- route.guardhook
- objecthook
- upload.guardhook
- onPresignedhook
- Upload to S3S3
- confirmGuardhook
- HeadObjectserver
- onConfirmedhook
route.guard: Evaluates session and permissions before key generation.object: Computes destination folder, key, metadata, ACL, storage class, cache control, or tags.upload.guard: Authorizes the upload after the server-owned key is assigned.onPresigned: Fires after the presigned URL is created.confirmGuard: Authorizes the confirmation request after bytes land in S3.onConfirmed: Reads verified metadata fromHeadObjectand persists the record.
Type reference
import type {
UploadGuardContext,
UploadOnConfirmedContext,
UploadConfirmGuardContext,
UploadObjectContext,
} from "@dimah-s3/server";UploadGuardContext
Runs after upload.object assigns the key (and after global / route guards).
Prop
Type
UploadOnConfirmedContext
Runs after HeadObject validates the uploaded file size and content type.
Prop
Type
UploadConfirmGuardContext
Runs before HeadObject when confirming single-shot uploads or completing multipart uploads.
Prop
Type
UploadObjectContext
Input passed to upload.object to generate custom destination keys and metadata.
Prop
Type
Frequently asked questions
Record it in onConfirmed, where contentLength comes from S3 HeadObject.
Presign payloads contain browser-declared values and can be spoofed.
dimah-s3 attempts a best-effort DeleteObject so a failed callback does not
leave an orphaned file. The original callback error is still returned.
Return metadata from upload.object:
avatar: route({
upload: {
object: async ({ request, file }) => ({
folder: "avatars",
metadata: {
originalName: file.name,
},
}),
},
});