npm package: @laravel/stream-vue
Description: Laravel streaming hooks for Vue
Author: Taylor Otwell
Latest version: 0.3.13

# Laravel Stream for Vue

<p align="left">
<a href="https://github.com/laravel/stream/actions/workflows/tests.yml"><img src="https://github.com/laravel/stream/actions/workflows/tests.yml/badge.svg" alt="Build Status"></a>
<a href="https://www.npmjs.com/package/@laravel/stream-vue"><img src="https://img.shields.io/npm/dt/@laravel/stream-vue" alt="Total Downloads"></a>
<a href="https://www.npmjs.com/package/@laravel/stream-vue"><img src="https://img.shields.io/npm/v/@laravel/stream-vue" alt="Latest Stable Version"></a>
<a href="https://www.npmjs.com/package/@laravel/stream-vue"><img src="https://img.shields.io/npm/l/@laravel/stream-vue" alt="License"></a>
</p>

Easily consume streams in your Vue application.

## Installation

```bash
npm install @laravel/stream-vue
```

## Streaming Responses

> [!IMPORTANT]
> The `useStream` hook is currently in Beta, the API is subject to change prior to the v1.0.0 release. All notable changes will be documented in the [changelog](./../../CHANGELOG.md).

The `useStream` hook allows you to seamlessly consume [streamed responses](https://laravel.com/docs/responses#streamed-responses) in your Vue application.

Provide your stream URL and the hook will automatically update `data` with the concatenated response as data is returned from your server:

```vue
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";

const { data, isFetching, isStreaming, send } = useStream("chat");

const sendMessage = () => {
    send({
        message: `Current timestamp: ${Date.now()}`,
    });
};
</script>

<template>
    <div>
        <div>{{ data }}</div>
        <div v-if="isFetching">Connecting...</div>
        <div v-if="isStreaming">Generating...</div>
        <button @click="sendMessage">Send Message</button>
    </div>
</template>
```

When sending data back to the stream, the active connection to the stream is canceled before sending the new data. All requests are sent as JSON `POST` requests.

The second argument given to `useStream` is an options object that you may use to customize the stream consumption behavior:

```ts
type StreamOptions = {
    id?: string;
    initialInput?: Record<string, any>;
    headers?: Record<string, string>;
    csrfToken?: string;
    json?: boolean;
    credentials?: RequestCredentials;
    onResponse?: (response: Response) => void;
    onData?: (data: string) => void;
    onCancel?: () => void;
    onFinish?: () => void;
    onError?: (error: Error) => void;
    onBeforeSend?: (request: RequestInit) => boolean | RequestInit | void;
};
```

`onResponse` is triggered after a successful initial response from the stream and the raw [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) is passed to the callback.

`onData` is called as each chunk is received, the current chunk is passed to the callback.

`onFinish` is called when a stream has finished and when an error is thrown during the fetch/read cycle.

`onBeforeSend` is called right before sending the request to the server and receives the `RequestInit` object as an argument. Returning `false` from this callback cancels the request, returning a [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object will override the existing `RequestInit` object.

By default, a request is not made the to stream on initialization. You may pass an initial payload to the stream by using the `initialInput` option:

```vue
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";

const { data } = useStream("chat", {
    initialInput: {
        message: "Introduce yourself.",
    },
});
</script>

<template>
    <div>{{ data }}</div>
</template>
```

To cancel a stream manually, you may use the `cancel` method returned from the hook:

```vue
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";

const { data, cancel } = useStream("chat");
</script>

<template>
    <div>
        <div>{{ data }}</div>
        <button @click="cancel">Cancel</button>
    </div>
</template>
```

Each time the `useStream` hook is used, a random `id` is generated to identify the stream. This is sent back to the server with each request in the `X-STREAM-ID` header.

When consuming the same stream from multiple components, you can read and write to the stream by providing your own `id`:

```vue
<!-- App.vue -->
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";
import StreamStatus from "./StreamStatus.vue";

const { data, id } = useStream("chat");
</script>

<template>
    <div>
        <div>{{ data }}</div>
        <StreamStatus :id="id" />
    </div>
</template>
```

```vue
<!-- StreamStatus.vue -->
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";

const props = defineProps<{
    id: string;
}>();

const { isFetching, isStreaming } = useStream("chat", { id: props.id });
</script>

<template>
    <div>
        <div v-if="isFetching">Connecting...</div>
        <div v-if="isStreaming">Generating...</div>
    </div>
</template>
```

The `useJsonStream` hook is identical to the `useStream` hook except that it will attempt to parse the data as JSON once it has finished streaming:

```vue
<script setup lang="ts">
import { useJsonStream } from "@laravel/stream-vue";

type User = {
    id: number;
    name: string;
    email: string;
};

const { data, send } = useJsonStream<{ users: User[] }>("users");

const loadUsers = () => {
    send({
        query: "taylor",
    });
};
</script>

<template>
    <div>
        <ul>
            <li v-for="user in data?.users" :key="user.id">
                {{ user.id }}: {{ user.name }}
            </li>
        </ul>
        <button @click="loadUsers">Load Users</button>
    </div>
</template>
```

## Event Streams (SSE)

The `useEventStream` hook allows you to seamlessly consume [Server-Sent Events (SSE)](https://laravel.com/docs/responses#event-streams) in your Vue application.

Provide your stream URL and the hook will automatically update the `message` with the concatenated response as messages are returned from your server:

```vue
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";

const { message } = useEventStream("/stream");
</script>

<template>
    <div>{{ message }}</div>
</template>
```

You also have access to the array of message parts:

```vue
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";

const { messageParts } = useEventStream("/stream");
</script>

<template>
    <ul>
        <li v-for="message in messageParts">
            {{ message }}
        </li>
    </ul>
</template>
```

If you'd like to listen to multiple events:

```vue
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";

useEventStream("/stream", {
    eventName: ["update", "create"],
    onMessage: (event) => {
        if (event.type === "update") {
            // Handle update
        } else {
            // Handle create
        }
    },
});
</script>
```

The second parameter is an options object where all properties are optional (defaults are shown below):

```vue
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";

const { message } = useEventStream("/stream", {
    event: "update",
    onMessage: (message) => {
        //
    },
    onError: (error) => {
        //
    },
    onComplete: () => {
        //
    },
    endSignal: "</stream>",
    glue: " ",
    replace: false,
});
</script>
```

You can close the connection manually by using the returned `close` function:

```vue
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";
import { onMounted } from "vue";

const { message, close } = useEventStream("/stream");

onMounted(() => {
    setTimeout(() => {
        close();
    }, 3000);
});
</script>

<template>
    <div>{{ message }}</div>
</template>
```

The `clearMessage` function may be used to clear the message content that has been received so far:

```vue
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";
import { onMounted } from "vue";

const { message, clearMessage } = useEventStream("/stream");

onMounted(() => {
    setTimeout(() => {
        clearMessage();
    }, 3000);
});
</script>

<template>
    <div>{{ message }}</div>
</template>
```

## License

Laravel Stream is open-sourced software licensed under the [MIT license](LICENSE.md).