Supabase Database
Run Hot Updater's database on Supabase PostgreSQL.
Installation
npm install @hot-updater/supabase --save-devAn Edge Function that composes createHotUpdater, as in
Edge runtime usage, depends on @hot-updater/server
as well as @hot-updater/supabase.
Setup
Set up the backend with an AI agent or the interactive CLI, then use the configuration below. Managed setup applies the migration for you.
The settings below connect to an initialized database; they do not create its
tables or RPC function, and hot-updater db migrate does not run for
Supabase. To prepare a project yourself, copy
0001_hot-updater_1.0.0.sql from the installed
@hot-updater/supabase package's supabase/migrations directory into your
project's supabase/migrations directory, then apply it with the Supabase CLI
linked to that project:
npx supabase db pushThe migration creates the tables of core and of the managed Edge Function's
plugins, Insights, API keys, and Remote Config, under the hot_updater_v1_
prefix, with
row-level security on every table, the write guard table, and the
hot_updater_v1_apply function, then writes the schema settings rows last.
An Edge Function that runs other
plugins
also needs their tables, and the apply function must be allowed to reach them.
hot-updater db generate writes a migration that does both to
supabase/migrations/<timestamp>_hot-updater.sql; apply it with
npx supabase db push. It reads a Node module that passes the function's
plugins to createHotUpdater with supabaseDatabase from
@hot-updater/supabase.
Schema settings
Before its first read or write, the database checks the settings rows
schema.engine and schema.core in one batch read, and the server checks
schema.<id> for each plugin it runs, such as schema.insights and
schema.apiKeys, in another. While one is missing or different, or PostgREST cannot find the
apply function, it throws HotUpdaterSchemaMigrationRequiredError, and the
server's handlers answer 503. A running server checks again on its next
request, so it recovers once the migration has run.
hot-updater init stops when the project already holds an incompatible Hot
Updater schema.
How the database is reached
supabaseDatabase(config) returns an engine database, { name, adapter },
which defineConfig and createHotUpdater both accept. Its adapter is Hot
Updater's shared SQL core for PostgreSQL, behind the schema settings check.
Every read and write calls one function with the service role key:
hot_updater_v1_apply(p_statements jsonb). It runs the SQL core's statements
in the caller's transaction, so a write, sent as one call, commits
atomically. The function runs as SECURITY INVOKER with a fixed
search_path, and only service_role may execute it. It accepts only the SQL
core's statement shapes on Hot Updater's tables, and every value arrives in
its jsonb parameter, never in the SQL text.
Rows past their tables' retention,
such as Insights events and rollups, need no schedule or pg_cron. The Edge
Function request whose write takes the hourly lease first deletes a bounded
batch of them through the apply RPC.
Configuration
interface SupabaseDatabaseConfig {
supabaseUrl: string; // Your Supabase project URL
supabaseServiceRoleKey: string; // Your Supabase service role or secret key
}Usage
These CLI examples load the HOT_UPDATER_SUPABASE_* variables written to
.env.hotupdater by managed setup. Keep that file's existing values.
import { existsSync } from "node:fs";
import { supabaseDatabase } from "@hot-updater/supabase";
import { defineConfig } from "hot-updater";
if (existsSync(".env.hotupdater")) {
process.loadEnvFile(".env.hotupdater");
}
export default defineConfig({
// ... build, storage, plugins, and updateStrategy
database: supabaseDatabase({
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
}),
});Edge runtime usage
Use the /edge subpath when composing createHotUpdater inside a Supabase
Edge Function. The provider keeps the same name while the subpath makes the
runtime boundary explicit:
import { createHotUpdater } from "@hot-updater/server";
import { apiKeys, insights, remoteConfig } from "@hot-updater/server/plugins";
import { supabaseDatabase } from "@hot-updater/supabase/edge";
export const hotUpdater = createHotUpdater({
database: supabaseDatabase({
supabaseUrl: Deno.env.get("SUPABASE_URL")!,
supabaseServiceRoleKey: Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
}),
storage, // The adapter hot-updater.config.ts uploads with, such as supabaseStorage(...).
plugins: [insights(), apiKeys(), remoteConfig()],
});insights() records app events, apiKeys() protects client routes with
the x-api-key header, and remoteConfig() serves Remote Config. The managed
Edge Function runs the same list. List the server's plugins in
hot-updater.config.ts too; see
CLI Configuration.
Complete example
The hot-updater.config.ts that hot-updater init writes for managed
Supabase, with Storage, the Postgres database, and the plugins the managed Edge
Function runs:
import { bare } from "@hot-updater/bare";
import { supabaseDatabase, supabaseStorage } from "@hot-updater/supabase";
import { apiKeys, insights, remoteConfig } from "hot-updater/plugins";
import { defineConfig } from "hot-updater";
import { existsSync } from "node:fs";
if (existsSync(".env.hotupdater")) {
process.loadEnvFile(".env.hotupdater");
}
export default defineConfig({
build: bare({ enableHermes: true }),
storage: supabaseStorage({
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
bucketName: process.env.HOT_UPDATER_SUPABASE_BUCKET_NAME!,
}),
database: supabaseDatabase({
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
}),
plugins: [apiKeys(), insights(), remoteConfig()],
updateStrategy: "appVersion", // or "fingerprint"
});