Quat Docs
Framework guides

Node.js

Use Quat with the AWS SDK for JavaScript v3.

Install

npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner dotenv

Environment

S3_ENDPOINT=https://storage.example.local
S3_ACCESS_KEY=your-access-key
S3_SECRET_KEY=your-secret-key
S3_REGION=us-east-1
S3_BUCKET=my-bucket
S3_USE_PATH_STYLE_ENDPOINT=true

Client

import "dotenv/config";
import { S3Client } from "@aws-sdk/client-s3";

export const s3 = new S3Client({
    endpoint: process.env.S3_ENDPOINT,
    region: process.env.S3_REGION || "us-east-1",
    credentials: {
        accessKeyId: process.env.S3_ACCESS_KEY || "",
        secretAccessKey: process.env.S3_SECRET_KEY || "",
    },
    forcePathStyle: process.env.S3_USE_PATH_STYLE_ENDPOINT !== "false",
});

export const bucket = process.env.S3_BUCKET || "";

Upload Object

import { readFile } from "node:fs/promises";
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { bucket, s3 } from "./s3";

export async function uploadObject() {
    const body = await readFile("./avatar.png");

    await s3.send(
        new PutObjectCommand({
            Bucket: bucket,
            Key: "users/123/avatar.png",
            Body: body,
            ContentType: "image/png",
        }),
    );

    console.log("Uploaded users/123/avatar.png");
}

Download Object

import { writeFile } from "node:fs/promises";
import { GetObjectCommand } from "@aws-sdk/client-s3";
import { bucket, s3 } from "./s3";

export async function downloadObject() {
    const response = await s3.send(
        new GetObjectCommand({
            Bucket: bucket,
            Key: "users/123/avatar.png",
        }),
    );

    if (!response.Body) throw new Error("Object body was empty");

    const bytes = await response.Body.transformToByteArray();
    await writeFile("./downloaded-avatar.png", bytes);
}

List Objects

import { ListObjectsV2Command } from "@aws-sdk/client-s3";
import { bucket, s3 } from "./s3";

export async function listObjects() {
    const response = await s3.send(
        new ListObjectsV2Command({
            Bucket: bucket,
            Prefix: "users/123/",
        }),
    );

    for (const item of response.Contents || []) {
        console.log(item.Key, item.Size);
    }
}

Delete Object

import { DeleteObjectCommand } from "@aws-sdk/client-s3";
import { bucket, s3 } from "./s3";

export async function deleteObject() {
    await s3.send(
        new DeleteObjectCommand({
            Bucket: bucket,
            Key: "users/123/avatar.png",
        }),
    );
}

Presigned URL

The customer app code includes backend-generated upload intents and download links. If your Quat storage endpoint accepts standard S3 signed requests, you can generate SDK presigned URLs:

import { GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { bucket, s3 } from "./s3";

export async function createDownloadUrl() {
    return getSignedUrl(
        s3,
        new GetObjectCommand({
            Bucket: bucket,
            Key: "users/123/avatar.png",
        }),
        { expiresIn: 900 },
    );
}

Note: Verify presigned URL support against your deployment. This repository confirms console download links, but does not include the full storage server implementation.

Common Node.js Errors

ErrorFix
SignatureDoesNotMatchCheck endpoint, region, keys, and forcePathStyle.
InvalidAccessKeyIdReveal or regenerate keys in Quat and update .env.
NoSuchBucketUse the real bucket name, not only the friendly name.
DNS error with bucket subdomainSet forcePathStyle: true.

On this page