# TransformStream

The `TransformStream` interface is part of the Streams API of Azion Runtime. It is a concrete implementation of the pipe chain transform stream concept: you pass it to the [`pipeThrough()`](/en/documentation/devtools/runtime/api-reference/readable-stream/#methods) method of a `ReadableStream` to convert a stream of data from one format into another. Use it to decode or encode video frames, decompress data, or convert a stream from XML to JSON. `TransformStream` is a transferable object. For more information, refer to [TransformStream](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) on MDN Web Docs.

---

## Constructor

The [`TransformStream()`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream/TransformStream) constructor creates and returns a transform stream object:

```javascript
new TransformStream()
```

The constructor takes an optional transformation object and optional queuing strategies for its streams. Without a transformation object, data passes through the stream unchanged.

The transformation object carries the transformation algorithm. Its `transform(chunk, controller)` method receives each chunk written to the stream, and `controller.enqueue()` passes a chunk to the readable side. A transformation object can also define `start()` and `flush()` methods, as the [Example](#example) section shows.

For a queuing strategy, Azion Runtime defines `CountQueuingStrategy` but not `ByteLengthQueuingStrategy`: a reference to `ByteLengthQueuingStrategy` throws `ReferenceError: ByteLengthQueuingStrategy is not defined`.

---

## Properties

A transform stream has two ends. Together they form the pair that `pipeThrough()` accepts:

| Property                                                                                | Type             | Description                                                                    |
| --------------------------------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------ |
| [`readable`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream/readable) | `ReadableStream` | The readable end of the transform stream. It returns the transformed chunks.   |
| [`writable`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream/writable) | `WritableStream` | The writable end of the transform stream. It receives the chunks to transform. |

---

## Example

This handler defines `AnyToU8Stream`, a transform stream that passes every chunk it receives through as a `Uint8Array`. It pipes a string, an array of numbers, and a `Uint16Array` through the stream, then returns the bytes of each output chunk:

```javascript
export default {
  async fetch(request, env, ctx) {
    const transformContent = {
      start() {}, // required.
      async transform(chunk, controller) {
        chunk = await chunk;
        switch (typeof chunk) {
          case 'object':
            // just say the stream is done
            if (chunk === null) {
              controller.terminate();
            } else if (ArrayBuffer.isView(chunk)) {
              controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
            } else if (
              Array.isArray(chunk) &&
              chunk.every((value) => typeof value === 'number')
            ) {
              controller.enqueue(new Uint8Array(chunk));
            } else if (
              typeof chunk.valueOf === 'function' &&
              chunk.valueOf() !== chunk
            ) {
              this.transform(chunk.valueOf(), controller); // hack
            } else if ('toJSON' in chunk) {
              this.transform(JSON.stringify(chunk), controller);
            }
            break;
          case 'symbol':
            controller.error("Cannot send a symbol as a chunk part")
            break
          case 'undefined':
            controller.error("Cannot send undefined as a chunk part")
            break
          default:
            controller.enqueue(this.textencoder.encode(String(chunk)))
            break
        }
      },
      flush() { /* do any destructor work here */ }
    }

    class AnyToU8Stream extends TransformStream {
      constructor() {
        super({...transformContent, textencoder: new TextEncoder()})
      }
    }

    const stream = new ReadableStream({
      start(c) {
        c.enqueue('hi');
        c.enqueue([1, 2]);
        c.enqueue(new Uint16Array([65]));
        c.close();
      },
    }).pipeThrough(new AnyToU8Stream());

    const chunks = [];
    for await (const chunk of stream) chunks.push(Array.from(chunk));
    return Response.json(chunks);
  },
};
```

The function returns these bytes for the three output chunks. The string `'hi'` becomes its encoded bytes, the number array becomes a `Uint8Array` with the same values, and the `Uint16Array` comes out as the two bytes of its buffer:

```json
[
 [
  104,
  105
 ],
 [
  1,
  2
 ],
 [
  65,
  0
 ]
]
```

---

## Related resources

- [ReadableStream](/en/documentation/devtools/runtime/api-reference/readable-stream.md): The stream whose `pipeThrough()` method sends data through a transform stream.
- [WritableStream](/en/documentation/devtools/runtime/api-reference/writable-stream.md): The stream type of the `writable` end, and how to write chunks to it.
- [Encoding](/en/documentation/devtools/runtime/api-reference/encoding.md): The `TextEncoder` that the example uses to turn strings into bytes.
- [Web APIs](/en/documentation/devtools/runtime/api-reference/javascript.md): The other Web APIs that Azion Runtime supports.
