# JavaScript (AWS SDK v3) (/tools/s3-gateway/quickstarts/javascript)

Prefer to run it? [shelby-quickstart](https://github.com/shelby/shelby-quickstart/tree/main/clients/javascript)
has this page as a script that checks every step and cleans up after itself.

## Before you start

* Node.js 22.18 or later, which runs TypeScript files directly
* A project set up for ES modules, with the AWS SDK installed:

  ```bash
  npm init -y
  npm pkg set type=module
  npm install @aws-sdk/client-s3 @aws-sdk/lib-storage
  ```

Copy your bucket name, and an access key ID and secret for that bucket, from the
[console](https://console.shelby.xyz). Export them so the commands below can use them:

```bash
export SHELBY_ACCESS_KEY_ID="<your access key ID, SHLB…>"
export SHELBY_SECRET_ACCESS_KEY="<your secret>"
export SHELBY_BUCKET="<your bucket name>"
```

<Steps>
  <Step>
    ### Create a client

    ```ts title="shelby-s3.ts"
    import { S3Client } from "@aws-sdk/client-s3";

    export const s3 = new S3Client({
      endpoint: "https://s3.beta.shelby.xyz",
      region: "default",
      credentials: {
        accessKeyId: process.env.SHELBY_ACCESS_KEY_ID!,
        secretAccessKey: process.env.SHELBY_SECRET_ACCESS_KEY!,
      },
      forcePathStyle: true,
      requestChecksumCalculation: "WHEN_REQUIRED",
      responseChecksumValidation: "WHEN_REQUIRED",
    });

    export const Bucket = process.env.SHELBY_BUCKET!;
    ```

    `requestChecksumCalculation` is optional. It stops the SDK from computing CRC checksums
    that Shelby accepts but doesn't verify.

    `responseChecksumValidation` is needed for presigned download links. Without it, the SDK adds
    `x-amz-checksum-mode` to them, which Shelby refuses with `501 NotImplemented`. Shelby returns
    no response checksums, so no check is lost.
  </Step>

  <Step>
    ### Upload and read back

    ```ts title="quickstart.ts"
    import {
      GetObjectCommand,
      ListObjectsV2Command,
      PutObjectCommand,
    } from "@aws-sdk/client-s3";
    import { Bucket, s3 } from "./shelby-s3.ts";

    const Key = "examples/hello.txt";

    await s3.send(new PutObjectCommand({ Bucket, Key, Body: "Hello, Shelby\n" }));

    const object = await s3.send(new GetObjectCommand({ Bucket, Key }));
    console.log(await object.Body?.transformToString());

    // A new object can take a few seconds to appear in listings.
    const listing = await s3.send(
      new ListObjectsV2Command({ Bucket, Prefix: "examples/" }),
    );
    for (const item of listing.Contents ?? []) console.log(item.Key, item.Size);
    ```

    Run it:

    ```bash
    node quickstart.ts
    ```
  </Step>

  <Step>
    ### Share a download link

    ```bash
    npm install @aws-sdk/s3-request-presigner
    ```

    ```ts title="share.ts"
    import { GetObjectCommand } from "@aws-sdk/client-s3";
    import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
    import { Bucket, s3 } from "./shelby-s3.ts";

    const url = await getSignedUrl(
      s3,
      new GetObjectCommand({ Bucket, Key: "examples/hello.txt" }),
      { expiresIn: 300 },
    );

    const response = await fetch(url); // sends no credentials
    console.log(response.status, await response.text());
    ```

    ```bash
    node share.ts
    ```

    Anyone with `url` can download the object for the next 5 minutes, without credentials. Until
    it expires, treat the link like a password. See
    [Presigned URLs](/tools/s3-gateway/presigned-urls).
  </Step>

  <Step>
    ### Clean up

    ```ts title="clean-up.ts"
    import { DeleteObjectCommand } from "@aws-sdk/client-s3";
    import { Bucket, s3 } from "./shelby-s3.ts";

    await s3.send(new DeleteObjectCommand({ Bucket, Key: "examples/hello.txt" }));
    ```

    ```bash
    node clean-up.ts
    ```
  </Step>
</Steps>

## Upload large files

`Upload` from `@aws-sdk/lib-storage` splits large bodies into parts, 5 MiB each by default.
Set `partSize` to at least 10 MiB, because smaller parts each [count as 10 MiB](/protocol/pricing#the-10-mib-minimum) of
storage, and large enough for your largest file to fit in Shelby's 3,000 parts:

```ts
import { createReadStream } from "node:fs";
import { Upload } from "@aws-sdk/lib-storage";
import { Bucket, s3 } from "./shelby-s3.ts";

await new Upload({
  client: s3,
  params: {
    Bucket,
    Key: "data/dataset.parquet",
    Body: createReadStream("dataset.parquet"),
  },
  partSize: 64 * 1024 * 1024, // 64 MiB parts
}).done();
```

## What's different with the AWS SDK for JavaScript

* **Run it on a server.** Your secret must never reach a browser. To let a browser download
  an object, give it a [presigned URL](/tools/s3-gateway/presigned-urls) instead. Browsers
  can't upload, because Shelby has no presigned uploads.
* **`CopyObjectCommand` fails with `501`.** Download and upload again.
* **`DeleteObjectsCommand` doesn't work yet.** The SDK sends batch deletes in a form Shelby
  doesn't recognise, so nothing is deleted. Delete keys one at a time with
  `DeleteObjectCommand`. See [Known compatibility issues](/tools/s3-gateway/known-issues).
* **Authentication failures surface as XML parse errors.** Shelby returns a plain-text
  `401`, which the SDK can't parse. See
  [Troubleshooting](/tools/s3-gateway/troubleshooting#authentication-errors).

[Compatibility matrix](/tools/s3-gateway/compatibility) ·
[Troubleshooting](/tools/s3-gateway/troubleshooting)
