Streaming & Upload

Core's callApiStream reads NDJSON responses as line-by-line JSON and emits them in order from the Observable.


Direct usage of callApiStream in Core.

Use callApiStream when the server returns NDJSON (one JSON per line). If the Accept header is not present, it defaults to application/x-ndjson, and other media types can be overridden with per-request headers.

ts
export const streamEvents = () => {
  return client.callApiStream<{
    eventName: string;
    content: unknown;
  }>({
    url: "/api/event/stream",
    method: "get",
    // headers: { Accept: "application/x-ndjson" },
  });
};

NDJSON response format.

text
{"status":"STARTED","progress":0}
{"status":"PROGRESS","progress":35}
{"status":"PROGRESS","progress":80}
{"status":"DONE","progress":100}

callApiStreamSplits the response based on line breaks and parses each line as JSON. It expects NDJSON, not the standard SSE data: frame format.

Observable subscription and unsubscription.

tsx
const subscription = streamEvents().subscribe({
  next: (event) => {
    setProgress(event.content);
  },
  error: (error) => {
    setErrorMessage(error.message);
  },
  complete: () => {
    setRunning(false);
  },
});

return () => subscription.unsubscribe();

Calling unsubscribe() will also cancel the internal ReadableStream reader. Unsubscribe in React effects or cleanup tasks.

Optional: Use the generated stream service.

In projects using the OpenAPI generator, you can make the generated service call callApiStream for the stream endpoint. It can be imported and subscribed to in the same way as a regular generated service.

tsx
const subscription = jobProgress({
  path: { jobId },
}).subscribe({
  next: (event) => {
    setProgress(event.progress || 0);
  },
  error: (error) => {
    setErrorMessage(error.message);
  },
  complete: () => {
    setRunning(false);
  },
});

return () => subscription.unsubscribe();

uploadFile: PUT file to specified URL.

uploadFile.ts
ts
service.uploadFile({
  file,
  url: signedUploadUrl,
  headers: {
    "Content-Type": file.type || "application/octet-stream",
    "If-None-Match": "*",
  },
}).subscribe();
© 2026 Byeolnaerim. All rights reserved.IntroductionPrivacy Policy