AWS SDK v3 Node.js Streams Shouldn't Be This Hard (Here's the Fix)

Posted on May 9, 2025 by Ben Limmer Avatar image of Ben Limmer

AWS SDK v3 made working with streams in Node.js a lot more difficult than it was in v2. This article shows you how to simplify your migration from AWS SDK v2 to v3.

Why Node Streams Break in AWS SDK v3

In AWS SDK v3, the StreamingBlobPayloadOutputTypes type is used to represent streams that can be returned in both browser and Node.js environments.

type StreamingBlobPayloadOutputTypes =
| NodeJsRuntimeStreamingBlobPayloadOutputTypes
| BrowserRuntimeStreamingBlobPayloadOutputTypes;

When you’re working in an application that will only be run in Node.js, this union type is painful to deal with.

For example, consider this code that uses the GetObjectCommand to download a file from S3:

import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
export async function processFromS3(bucket: string, key: string) {
const s3 = new S3Client({});
const result = await s3.send(
new GetObjectCommand({
Bucket: bucket,
Key: key,
}),
);
const stream = result.Body;
if (stream) {
// Does not work
// Property 'destroy' does not exist on type 'StreamingBlobPayloadOutputTypes'.
// Property 'destroy' does not exist on type 'Blob & SdkStreamMixin'.ts(2339)
stream.destroy();
}
}

This can be frustrating, especially when you’re sure you’re dealing with a Node.js stream.

Fixing It with NodeJsClient Type Cast

Luckily, there’s an underdocumented way to tell AWS SDK v3 what environment you’re running in, so you don’t have to manually narrow stream types yourself. The NodeJsClient type tells AWS SDK v3 to use Node.js-specific stream APIs when returning streams from these clients.

import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { type NodeJsClient } from "@smithy/types";
export async function processFromS3(bucket: string, key: string) {
const s3 = new S3Client({}) as NodeJsClient<S3Client>; // cast to NodeJsClient
const result = await s3.send(
new GetObjectCommand({
Bucket: bucket,
Key: key,
}),
);
const stream = result.Body; // automatically type-narrowed to SdkStream<IncomingMessage>
if (stream) {
// No longer flags a type error
stream.destroy();
}
}

Save yourself some hassle! Just cast to NodeJsClient<Client> and move on 🎉