Streaming with StreamResponse Class

Text Streaming with StreamResponse Class

In some cases it's too hard to use generators to implement response streaming. Vovk.ts introduces StreamResponse class inherited from Response class that uses TransformStream#readable as body and adds required HTTP headers. It's a lower-level API that is used behind the scenes to implement generator logic described at previous section. Service method at this case is implemented as a regular function that accepts StreamResponse instance as a pointer to send messages manually.

There is what the streaming service might look like:

/src/modules/stream/StreamService.ts
import type { StreamResponse } from 'vovk';
 
export type Token = { message: string };
 
export default class StreamService {
  static async streamTokens(resp: StreamResponse<Token>) {
    const tokens: Token[] = [
      { message: 'Hello,' },
      { message: ' World' },
      { message: '!' },
    ];
 
    for (const token of tokens) {
      await new Promise((resolve) => setTimeout(resolve, 300));
      resp.send(token);
    }
 
    resp.close();
  }
}

As you can see tokens are sent using StreamResponse#send method and, when the stream is completed, it needs to be closed with StreamResponse#close.

The Controller Class returns an instance of StreamResponse and the streaming is performed a floating Promise above the return statement.

import { prefix, get, StreamResponse, type VovkRequest } from 'vovk';
import StreamService, { type Token } from './StreamService';
 
@prefix('stream')
export default class StreamController {
  @get('tokens')
  static async streamTokens() {
    const resp = new StreamResponse<Token>();
 
    void StreamService.streamTokens(resp);
 
    return resp;
  }
}

StreamResponse class also provides throw methods that safely closes the stream and makes the client to re-throw the received error.

await resp.throw(new Error('Stream error'));

Live Text Streaming Example with StreamResponse Class

/src/modules/stream-response-object/StreamService.ts
import type { StreamResponse } from 'vovk';
 
export type Token = { message: string };
 
export default class StreamService {
  static async streamTokens(resp: StreamResponse<Token>) {
    const tokens: Token[] = [
      { message: 'Hello,' },
      { message: ' World' },
      { message: ' from' },
      { message: ' Stream' },
      { message: '!' },
    ];
 
    for (const token of tokens) {
      resp.send(token);
      await new Promise((resolve) => setTimeout(resolve, 300));
    }
 
    await resp.close();
  }
}

Source code (opens in a new tab)