Easily consume streams in your Vue application.
npm install @laravel/stream-vue
[!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.
The useStream
hook allows you to seamlessly consume 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:
<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:
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 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
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:
<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:
<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
:
<!-- 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>
<!-- 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:
<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>
The useEventStream
hook allows you to seamlessly consume Server-Sent Events (SSE) 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:
<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:
<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:
<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):
<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:
<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:
<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>
Laravel Stream is open-sourced software licensed under the MIT license.