HotupdaterHot Updater
Database Adapters

Drizzle

Build a self-hosted Hot Updater server with Drizzle ORM.

Before you start

Use this recipe in the server project from the custom-server quick start or your existing backend. Make the database and storage environment variables available to the server and schema commands. The React Native project's deploy configuration is a separate step.

drizzleAdapter supports PostgreSQL, MySQL 8.0 or later, and SQLite, through these Drizzle drivers:

DatabaseDrivers
PostgreSQLnode-postgres, postgres-js, PGlite, Neon over WebSockets
MySQLmysql2
SQLitelibSQL, better-sqlite3, bun:sqlite

Hot Updater runs every write in an interactive transaction. On first use, the adapter checks that the driver can run one and throws DrizzleTransactionUnsupportedError for a driver that cannot, such as Neon over HTTP. CockroachDB and SQL Server are not supported.

Installation

Install Drizzle ORM and SQLite driver.

npm install @hot-updater/server @hot-updater/aws drizzle-orm better-sqlite3
npm install --save-dev hot-updater drizzle-kit @types/better-sqlite3

For other databases, see Drizzle's adapter documentation.

Setup

Create a database connection file:

src/drizzle.ts
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";

const sqlite = new Database("./data/database.db");
export const db = drizzle(sqlite);

Hot Updater reads and writes through SQL, so the Drizzle instance does not need Hot Updater's schema.

Hot Updater configuration

Create the Hot Updater instance:

src/hotUpdater.ts
import { createHotUpdater } from "@hot-updater/server";
import { drizzleAdapter } from "@hot-updater/server/adapters/drizzle"; 
import { apiKeys, insights } from "@hot-updater/server/plugins";
import { s3Storage } from "@hot-updater/aws";
import { db } from "./drizzle";

export const hotUpdater = createHotUpdater({
  database: drizzleAdapter({ db, provider: "sqlite" }), 
  plugins: [insights(), apiKeys()],
  storage: s3Storage({
    region: "auto",
    endpoint: process.env.R2_ENDPOINT,
    credentials: {
      accessKeyId: process.env.R2_ACCESS_KEY_ID!,
      secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
    },
    bucketName: process.env.R2_BUCKET_NAME!,
  }),
});

drizzleAdapter takes db, a Drizzle database or a function that returns one on first use, and provider: "postgresql", "mysql" or "sqlite", matching the driver. It returns an engine database with the schema generator and migrator that hot-updater db runs. On better-sqlite3 and bun:sqlite, which run synchronously, the adapter runs one statement at a time and begins each transaction with BEGIN IMMEDIATE. insights() records app events, and apiKeys() protects the client routes with the x-api-key header.

Schema generation

Generate the Drizzle schema for Hot Updater tables.

mkdir -p data
npx hot-updater db generate src/hotUpdater.ts --yes

This command writes hot-updater-schema.ts at the project root: every table, key and index, with each column typed exactly as Hot Updater's SQL schema declares it, and no foreign keys. The tables are core's and those of the server's plugins.

Point Drizzle Kit at the generated file and the same SQLite database. If your config already lists schema files, add ./hot-updater-schema.ts to its schema array:

drizzle.config.ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./hot-updater-schema.ts", 
  out: "./drizzle",
  dialect: "sqlite",
  dbCredentials: {
    url: "./data/database.db",
  },
});

Apply the schema, then write the schema settings, before starting the server:

npx drizzle-kit push
npx hot-updater db migrate src/hotUpdater.ts --yes

Drizzle Kit creates the tables. db migrate then writes only the settings rows. It asks you to apply the tables first when they are missing, and it refuses a database created by an older Hot Updater schema instead of converting it. The server checks the settings rows before its first read or write; until they exist, it throws HotUpdaterSchemaMigrationRequiredError and its handlers answer 503. A running server checks again on its next request. Run all three commands again after upgrading Hot Updater.

Need plain SQL to apply yourself? See SQL Export for standalone SQL generation with the --sql flag.

CLI configuration

After the schema is applied, create the client API key using this same direct server configuration. Then connect the React Native project's CLI to the protected admin mount. Its storage configuration must match a server entry, and its plugins must list the server's plugins.

API endpoints

The adapter persists data; your framework mounts the HTTP handlers. Continue with the Hono, Express or Elysia recipe. See the handler boundaries and repository contract for client and admin access.

Finish with Set up your app and Test an OTA update.

On this page