Skip to main content
Some AWS operations return a byte stream instead of a fully buffered response body. The most common example is S3’s GetObject, where the response Body field is a stream of the object’s bytes rather than a string or buffer. In v3, stream response fields are enhanced with a set of helper methods defined by SdkStreamMixin. These methods are merged onto the stream object at runtime using Object.assign, regardless of whether you are running in Node.js or a browser.

Which commands return streams

Operations that return a streaming body are indicated by the <SdkStream> annotation in the API reference docs. At the type level, the output type is wrapped in WithSdkStreamMixin<T, StreamKey>. Common examples:
ServiceOperationStream field
S3GetObjectBody
S3SelectObjectContentPayload
Bedrock RuntimeInvokeModelWithResponseStreambody

SdkStreamMixin methods

Three collection methods are added to every stream response field:
transformToString
(encoding?: string) => Promise<string>
Reads the entire stream and returns it as a string. The default encoding is utf-8. Pass a different encoding (for example, "base64") as the first argument.
const bodyAsString = await response.Body.transformToString();
const bodyAsBase64 = await response.Body.transformToString("base64");
transformToByteArray
() => Promise<Uint8Array>
Reads the entire stream and returns it as a Uint8Array. Use this when you need binary data.
const bytes = await response.Body.transformToByteArray();
transformToWebStream
() => ReadableStream
Returns a Web Streams API ReadableStream. Use this to pipe the data elsewhere without buffering it in memory.
const webStream = response.Body.transformToWebStream();
await webStream.pipeTo(writableDestination);
Each of these methods can only be called once per stream. Calling any collection method a second time throws an error—streams cannot be rewound. Save the result from the first call.
const bodyAsString = await bodyStream.transformToString(); // ✅
const __error__ = await bodyStream.transformToString();    // throws

S3 GetObject example

import { S3 } from "@aws-sdk/client-s3";

const client = new S3({});

const getObjectResult = await client.getObject({
  Bucket: "my-bucket",
  Key: "my-key",
});

// env-specific stream with added mixin methods
const bodyStream = getObjectResult.Body;

// one-time transform
const bodyAsString = await bodyStream.transformToString();

Platform differences

The underlying stream type varies by runtime:
  • Node.js: http.IncomingMessage (a Node.js Readable stream)
  • Browsers: The Response body from the Fetch API (a Web ReadableStream)
The SdkStreamMixin methods abstract over these differences. You can call transformToString(), transformToByteArray(), or transformToWebStream() the same way in both environments.

Always consume or discard streams

Failing to read or discard a streaming response body leaves the underlying socket open. In Node.js, this can lead to socket exhaustion—a condition where your application runs out of connections, causing slowdowns, memory leaks, or deadlocks.
const getObjectResponse = await client.send(new GetObjectCommand({ Bucket, Key }));

// ⚠️ The socket remains open if you don't handle the body
console.log(getObjectResponse.$metadata.httpStatusCode);
Always do one of the following after receiving a streaming response:
if (needContent) {
  // ✅ buffer the stream
  const bytes = await getObjectResponse.Body.transformToByteArray();
} else if (pipingElsewhere) {
  // ✅ pipe the stream (e.g. re-upload to S3)
  await s3Client.send(
    new PutObjectCommand({
      Bucket,
      Key: "copy",
      Body: getObjectResponse.Body,
    })
  );
} else {
  // ✅ discard the stream
  // .destroy() for Node.js Readable; .cancel() for Web ReadableStream
  await (getObjectResponse.Body.destroy?.() ?? getObjectResponse.Body.cancel?.());
}
If you only need object metadata (size, content type, last modified date), use HeadObject or GetObjectAttributes instead of GetObject. These operations do not return a body and avoid the socket concern entirely.

Cancelling in-flight stream requests

Pass an abortSignal to client.send() to cancel a streaming request before it completes. The promise rejects with an AbortError.
const { AbortController } = require("@aws-sdk/abort-controller");
const { S3Client, CreateBucketCommand } = require("@aws-sdk/client-s3");

const abortController = new AbortController();
const client = new S3Client({});

const requestPromise = client.send(
  new CreateBucketCommand({ Bucket: "my-bucket" }),
  { abortSignal: abortController.signal }
);

// Cancel at any point before the response is returned
abortController.abort();

// Rejects with AbortError
await requestPromise;
A practical browser example—cancelling an S3 upload via a button click:
const abortController = new AbortController();
const abortSignal = abortController.signal;

document.querySelector(".abort").addEventListener("click", () => {
  abortController.abort();
});

document.querySelector(".upload").addEventListener("click", async (file) => {
  const client = new S3Client(clientParams);
  try {
    await client.send(new PutObjectCommand(commandParams), { abortSignal });
  } catch (e) {
    if (e.name === "AbortError") {
      console.log("Upload aborted:", e.message);
    }
  }
});
The native browser AbortController works directly with client.send(). You do not need @aws-sdk/abort-controller in modern environments—it was provided for compatibility with older runtimes.

Build docs developers (and LLMs) love