A reusable Convex component for apps that use Mux for video. Sync your Mux video data with Convex and build video apps with a real-time database backing your catalog.
This package gives you:
- Convex tables for Mux
assets,uploads,liveStreams, andevents - Mutations to upsert/delete synced Mux objects
- App-level
videoMetadatastorage (userId, title, visibility, tags, custom fields) - Query helpers for catalog and user-facing video data
- A separate companion CLI package,
@mux/convex-mux-init, to scaffold app-level Convex wrappers (npx convex-mux-init)
npm i @mux/convex @mux/mux-node
npm i -D @mux/convex-mux-initIf you prefer not to install the scaffold CLI, you can run it directly with
npx:
npx @mux/convex-mux-init@latest --component-name muxnpx convex-mux-init --component-name mux
# or without installing it
npx @mux/convex-mux-init@latest --component-name muxThis creates:
convex/convex.config.tsconvex/migrations.tsconvex/muxWebhook.tsconvex/muxHttp.tsconvex/http.tsif your app does not already have one
If files already exist, the CLI skips them unless you pass --force.
Existing convex/http.ts is never overwritten.
npx convex env set MUX_TOKEN_ID <your_mux_token_id>
npx convex env set MUX_TOKEN_SECRET <your_mux_token_secret>npx convex dev
npx convex run migrations:backfillMux '{}'If npx convex-mux-init created convex/http.ts for you, this is already
done.
If your app already had convex/http.ts, wire the generated helper into your
existing router:
import { httpRouter } from "convex/server";
import { registerMuxHttpRoutes } from "./muxHttp";
const http = httpRouter();
registerMuxHttpRoutes(http);
export default http;In the Mux dashboard, create a webhook endpoint:
- URL for deployed app:
https://<your-deployment>.convex.site/mux/webhook - URL for local development: use a tunnel (e.g. ngrok or cloudflared) to
/mux/webhook
Copy the webhook signing secret and set it in Convex:
npx convex env set MUX_WEBHOOK_SECRET <your_mux_webhook_secret>Tables to check:
assetsuploadsliveStreamseventsvideoMetadata
- Backfill is a one-time catch-up for existing Mux objects.
- Webhooks keep your Convex tables updated in near real time as Mux state changes.
Without webhooks, data will drift over time.
This follows Convex component best practices:
@mux/convexis component-only (schema, queries, mutations)- Node runtime integration (Mux SDK, webhook verification, backfill) lives in app-level code in your project
@mux/convex-mux-initis the separate CLI package that scaffolds those app-level files for you
If you mount with a different name, for example:
app.use(mux, { name: "videoInfra" });Then regenerate wrappers with the matching name:
npx convex-mux-init --component-name videoInfra --force# regenerate wrappers
npx convex-mux-init --component-name mux --force
# run backfill with options
npx convex run migrations:backfillMux '{"maxAssets":500,"defaultUserId":"dev-user-1","includeVideoMetadata":true}'
# run against prod deployment
npx convex run --prod migrations:backfillMux '{"maxAssets":500}'Could not find function for 'migrations:backfillMux': Ensureconvex/migrations.tsexists and exportsbackfillMux, then runnpx convex dev.InvalidReference ... does not export [mux_node.backfillAssets]: Do not callcomponents.<name>.mux_node.*directly. Use the app-level wrappers generated bynpx convex-mux-init.- TypeScript
webhooks.unwrap ... Record<string, unknown>: Regenerate wrappers withnpx convex-mux-init --force. - TypeScript
request.headers.entries is not a function/property: Build headers withrequest.headers.forEach(...)inconvex/muxHttp.ts. - Webhooks route compiles but never updates tables: If
ingestMuxWebhookis generated asinternalAction, call it viainternal.muxWebhook.ingestMuxWebhook(notanyApi.*). Node APIs without "use node": Ensure Node runtime files start with"use node";.- Bundling fails on
node:fs/node:pathfrom@mux/convex/bin/*: Upgrade to the split packages and use@mux/convex-mux-initas the separate scaffold CLI.