With some trivial modifications (including a conversion to TypeScript), I copied and pasted the JS code from this Amazon S3 Node.js example into a React Native app that I'm developing with Expo and USB debugging on Android. I want my program to read from a JSON file in one of my Amazon S3 buckets.
import 'react-native-url-polyfill/auto'
import 'react-native-get-random-values'
import { Readable } from 'node:stream'
import { CognitoIdentityClient } from '@aws-sdk/client-cognito-identity'
import { fromCognitoIdentityPool } from '@aws-sdk/credential-provider-cognito-identity'
// Create service client module using ES6 syntax.
import {
S3Client,
GetObjectCommand,
GetObjectOutput
} from '@aws-sdk/client-s3'
// Set the AWS Region.
const region = 'us-east-1'
// Create an Amazon S3 service client object.
const client = new S3Client({
region,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region }),
identityPoolId: 'us-east-1:xxxxxx-xxx-4103-9936-b52exxxxfd6'
})
})
export const bucketParams = {
Bucket: 'bucket-name',
Key: 'filename.json'
}
export const run = async (): Promise<string> => {
// Create a helper function to convert a ReadableStream to a string.
const streamToString = (stream: Readable): Promise<string> =>
new Promise((resolve, reject) => {
const chunks: Uint8Array[] = []
stream.on('data', (chunk: Uint8Array): void => {chunks.push(chunk)})
stream.on('error', reject)
stream.on('end', (): void => resolve(Buffer.concat(chunks).toString('utf8')))
})
// Get the object from the Amazon S3 bucket. It is returned as a ReadableStream.
const data: GetObjectOutput = await client.send(new GetObjectCommand(bucketParams))
// Convert the ReadableStream to a string.
const bodyContents: string = await streamToString(data.Body as Readable)
return bodyContents
}
I've already confirmed (I think) that the const
variable data
is successfully assigned a proper value (an object, at least) before its property Body
is passed as an argument to streamToString
. In light of this, the line const data: GetObjectOutput = await client.send(new GetObjectCommand(bucketParams))
seems to do its job—assign to data
a JavaScript object of type GetObjectOutput
with a property Body
of the union type Readable | ReadableStream<any> | Blob | undefined
.
In order to test the initialization of bodyContents
, I wrote and called an anonymous function, as follows:
(async () => alert( await run() ))()
When I ran this test, the value of bodyContents
was not printed as expected. Instead, I got an error:
[Unhandled promise rejection: TypeError: stream.on is not a function. (In 'stream.on('data', function (chunk) {]
at Contents.tsx:154:16 in Promise$argument_0
at Contents.tsx:152:12 in streamToString
at Contents.tsx:163:57 in run
To see what might be causing this error, I command-clicked, within VS Code, the first call to the function on
inside the initialization of streamToString
, with the event 'data'
as its first argument. I was thereby directed to the corresponding declaration—along with 7 overloads—of on
, which is a method inside the class Readable
. This class is defined in the file stream.d.ts
from the Node core library (node_modules/@types/node/stream.d.ts
). Here is the corresponding declaration of on
, written in TypeScript:
on(event: 'data', listener: (chunk: any) => void): this;
Considering that my first call to stream.on
conforms to this declaration, I don't understand why stream.on
is not recognized as a function. I did import the class Readable
which contains the method's declaration, after all. To see that the function call conforms to the declaration, note that stream
is a Readable
object, and I pass in arguments of the forms 'data'
(that is, the exact string 'data'
) and (chunk: any) => void
, respectively. Uint8Array
falls under any
, of course, as does any type.
I realize that the implementations of on
may, for all I know, be missing from my project. I've yet to track down a single implementation worthy of the name in my project, despite my diligent command-clicking in VS Code. I'm taking it on faith that I don't have to implement the function myself, since there is no mention of that in the AWS docs. But if I do have to, that means I need to acquaint myself with the algorithm that this overload of on
is meant to implement. All I know is that it's supposed to read streams of data.
How do I get this bug out of my hair?