Skip to content

SupaCloud

@svadmin/supabase keeps the official Supabase adapters focused on data, auth, audit, and realtime. The optional @svadmin/supabase/supacloud subpath adds task-oriented platform APIs on top of a normal @supabase/supabase-js client.

@supacloud/js does not replace @supabase/supabase-js. It wraps an existing Supabase client and adds platform semantics such as:

  • task submission
  • task polling / waiting
  • dead-letter queue inspection
  • cancel / retry helpers
  • task status subscription

Keeping these APIs under @svadmin/supabase/supacloud avoids breaking the existing createSupabaseAuthProvider, createSupabaseLiveProvider, and createSupabaseAuditHandler signatures, while also keeping @supacloud/js optional for projects that only need plain Supabase.

Terminal window
bun add @svadmin/supabase @supabase/supabase-js @supacloud/js
import { createClient } from '@supabase/supabase-js';
import { createSupaCloudClient } from '@supacloud/js';
const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY,
);
const supacloud = createSupaCloudClient({
supabase,
managementApiUrl: import.meta.env.VITE_SUPACLOUD_API_URL,
projectRef: import.meta.env.VITE_SUPACLOUD_PROJECT_REF,
});

Use createSupaCloudTaskProvider() when you want a thin, task-focused API surface without changing your existing svadmin providers.

import { createSupaCloudTaskProvider } from '@svadmin/supabase/supacloud';
const taskProvider = createSupaCloudTaskProvider({ supacloud });
  • submit(taskName, options)
  • get(taskId)
  • list(params?)
  • listDlq(params?)
  • cancel(taskId)
  • retry(taskId)
  • subscribe(taskId, callback)
const task = await taskProvider.submit('aorist-ai/generate/crop', {
body: { image_id: 'img_123' },
idempotencyKey: 'crop-img_123-v1',
});
const finalState = await task.wait();
console.log(finalState.status);
const latest = await taskProvider.get('task_123');
const recent = await taskProvider.list({ limit: 20 });
const dlq = await taskProvider.listDlq({ limit: 20 });
await taskProvider.cancel('task_123');
await taskProvider.retry('task_123');

Use createSupaCloudTaskLiveProvider() when you want to bridge tasks.subscribe() into svadmin’s LiveProvider contract.

import { createSupaCloudTaskLiveProvider } from '@svadmin/supabase/supacloud';
const taskLiveProvider = createSupaCloudTaskLiveProvider({ supacloud });

This provider expects liveParams.taskId when subscribing:

const stop = taskLiveProvider.subscribe({
resource: 'tasks',
liveParams: { taskId: 'task_123' },
callback: (event) => {
console.log(event.type);
console.log(event.payload);
},
});
stop();

By default, task updates are mapped to:

{
type: 'UPDATE',
resource: 'tasks',
payload: task,
}

You can override this behavior with mapTaskToEvent:

const taskLiveProvider = createSupaCloudTaskLiveProvider({
supacloud,
resource: 'jobs',
mapTaskToEvent: (task, resource) => ({
type: task.status === 'queued' ? 'INSERT' : 'UPDATE',
resource,
payload: task,
}),
});

The recommended pattern is composition:

import {
createSupabaseAuthProvider,
createSupabaseDataProvider,
createSupabaseLiveProvider,
} from '@svadmin/supabase';
import {
createSupaCloudTaskProvider,
createSupaCloudTaskLiveProvider,
} from '@svadmin/supabase/supacloud';
const dataProvider = createSupabaseDataProvider(supabase);
const authProvider = createSupabaseAuthProvider(supabase);
const liveProvider = createSupabaseLiveProvider(supabase);
const taskProvider = createSupaCloudTaskProvider({ supacloud });
const taskLiveProvider = createSupaCloudTaskLiveProvider({ supacloud });

Use the standard providers for your admin CRUD flows, and use the SupaCloud helpers only where you need platform task semantics.