Skip to content

Hyperserve Adapter

The first-party adapter for the Hyperserve video API.

Terminal window
npm install @hyperserve/video-uploader-adapter-hyperserve

Use createHyperserveConfig to produce a ready-to-use UploadConfig:

import { createHyperserveConfig } from "@hyperserve/video-uploader-adapter-hyperserve";
import { composeValidators, maxFileSize } from "@hyperserve/video-uploader";
const config = createHyperserveConfig({
createUpload: async (file, options) =>
fetch("/api/create-upload", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: file.name, size: file.size, ...options }),
}).then((r) => r.json()),
completeUpload: async (videoId) => {
await fetch(`/api/complete-upload/${videoId}`, { method: "POST" });
},
pollVideoStatus: async (videoId) =>
fetch(`/api/video-status/${videoId}`).then((r) => r.json()),
uploadOptions: {
resolutions: ["480p", "1080p"],
isPublic: true,
},
validate: composeValidators(maxFileSize(500 * 1024 * 1024)),
});

The three callbacks call your backend, which proxies to Hyperserve using your API key; credentials never touch the browser.

type HyperserveUploadOptions = {
resolutions: string[];
isPublic: boolean;
thumbnail_timestamps_seconds?: number[];
custom_user_metadata?: Record<string, unknown>;
};

custom_user_metadata is forwarded on the create-video request and included in webhook payloads when processing completes. Use it to associate uploads with your app’s entities (post ID, product ID, etc.).

thumbnail_timestamps_seconds lets you request multiple thumbnail frames at specific points in the video. Each value is a non-negative number of seconds.

After upload, the Hyperserve adapter polls your backend until the video finishes transcoding. The polling interval defaults to 3 seconds. Override it:

const config = createHyperserveConfig({
// ...
pollVideoStatus: async (videoId) => {
const res = await fetch(`/api/video-status/${videoId}`);
return res.json();
},
pollingIntervalMs: 5000,
});

If you do not provide pollVideoStatus, files stay in "processing" indefinitely. For push delivery (webhook, SSE, Realtime broadcast), skip the poller and call updateFileStatus from useUpload() when your server signals completion. See Driving status from a webhook or SSE.

When uploadOptions come from a form (the user picks resolutions, toggles isPublic, etc.), build the config inside the component with useMemo so it reflects current state:

import { useMemo } from "react";
import { UploadProvider } from "@hyperserve/video-uploader";
import { createHyperserveConfig } from "@hyperserve/video-uploader-adapter-hyperserve";
function UploadModal({ isPublic, resolutions }: Props) {
const config = useMemo(
() =>
createHyperserveConfig({
createUpload: /* ... */,
completeUpload: /* ... */,
pollVideoStatus: /* ... */,
uploadOptions: { isPublic, resolutions },
}),
[isPublic, resolutions],
);
return (
<UploadProvider config={config}>
{/* ... */}
</UploadProvider>
);
}

UploadProvider keeps an internal configRef that updates on every render, so the config you pass is always current when a new upload starts. useMemo is for performance only: it avoids rebuilding the config object on every render.