Stream Handling
Stream endpoints return binary data (Blob) instead of JSON. You can use them with mutations to download files.
Basic Stream Mutation
Create a mutation from a stream endpoint:
// shared/endpoints/files.ts
export const downloadFileEndpoint = API.declareStream({
method: 'GET',
url: '/files/$fileId/download',
})
// client/mutations/files.ts
const downloadFileMutation = client.mutationFromEndpoint(downloadFileEndpoint, {
processResponse: (blob) => blob,
})
Usage
function DownloadButton({ fileId }: { fileId: string }) {
const { mutate, isPending, data } = downloadFileMutation()
// Handle download when data is received
useEffect(() => {
if (data) {
const url = URL.createObjectURL(data)
const a = document.createElement('a')
a.href = url
a.download = 'file.pdf'
a.click()
URL.revokeObjectURL(url)
}
}, [data])
return (
<button
onClick={() => mutate({ urlParams: { fileId } })}
disabled={isPending}
>
{isPending ? 'Downloading...' : 'Download'}
</button>
)
}
With onSuccess Callback
Define download logic at declaration time:
const downloadFileMutation = client.mutationFromEndpoint(downloadFileEndpoint, {
processResponse: (blob) => blob,
onSuccess: (blob) => {
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'file.pdf'
a.click()
URL.revokeObjectURL(url)
},
})
With Filename
function DownloadButton({ fileId, filename }: { fileId: string; filename: string }) {
const { mutate, isPending, data } = downloadFileMutation()
useEffect(() => {
if (data) {
const url = URL.createObjectURL(data)
const a = document.createElement('a')
a.href = url
a.download = filename
a.click()
URL.revokeObjectURL(url)
}
}, [data, filename])
return (
<button onClick={() => mutate({ urlParams: { fileId } })} disabled={isPending}>
Download
</button>
)
}
Preview Before Download
function FilePreview({ fileId }: { fileId: string }) {
const { mutate, isPending, data } = downloadFileMutation()
const [previewUrl, setPreviewUrl] = useState<string | null>(null)
useEffect(() => {
if (data) {
const url = URL.createObjectURL(data)
setPreviewUrl(url)
// Cleanup
return () => {
URL.revokeObjectURL(url)
setPreviewUrl(null)
}
}
}, [data])
const handleDownload = () => {
if (previewUrl) {
const a = document.createElement('a')
a.href = previewUrl
a.download = 'file.pdf'
a.click()
}
}
return (
<div>
<button onClick={() => mutate({ urlParams: { fileId } })} disabled={isPending}>
{isPending ? 'Loading...' : 'Preview'}
</button>
{previewUrl && (
<div>
<iframe src={previewUrl} />
<button onClick={handleDownload}>Download</button>
</div>
)}
</div>
)
}
Next Steps
- Multipart Mutations - Upload files
- Mutations - Basic mutations