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?