Overview
Since v0.22.0+Build a custom backend server for Hot Updater database with any framework and ORM.
Installation
Install the server package and standalone plugin.
npm install @hot-updater/server @hot-updater/standalone --save-devArchitecture
Self-hosted mode lets you control the database layer while using existing cloud storage plugins.
How it works:
- CLI: Uses
standaloneRepositoryin hot-updater.config.ts to send requests to your server - Server: Handles database operations with
@hot-updater/server - Storage: Uses existing plugins (AWS S3, Supabase, Cloudflare R2, Firebase)
This approach gives you full control over metadata while leveraging reliable cloud storage.
CLI Configuration
Configure your Hot Updater CLI to use the self-hosted database.
import { defineConfig } from "@hot-updater/core";
import { bare } from "@hot-updater/bare";
import { s3Storage } from "@hot-updater/aws";
import { standaloneRepository } from "@hot-updater/standalone";
export default defineConfig({
build: bare(),
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!,
}),
database: standaloneRepository({
baseUrl: "http://localhost:3000/hot-updater",
}),
});Storage Plugins
The storages field in your server configuration accepts storage plugins that
implement the runtime profile. Runtime storage plugins translate stored bundle
locations into client download URLs. They can also read small metadata files
directly from storage when bundle diffing data is available.
- When you run
npx hot-updater deployfrom yourhot-updater.config.ts, the bundle is uploaded and astorageUriis saved to the database - When the API returns bundle information, the server decodes the
storageUriusingruntime.getDownloadUrlto generate an HTTP(S) download URL - When diffing data is available, the server reads it directly from storage so the client can reuse unchanged files
- React Native clients download the update files using the returned URLs
The CLI storage field uses the node profile for upload/delete/local file
download operations. A plugin such as s3Storage can support both profiles,
while edge providers such as Cloudflare Workers may use a separate runtime
plugin that reads from platform bindings.
Multiple Storage Plugins
storages is an array, allowing you to configure multiple storage providers simultaneously. Each plugin acts as a decoder for its specific storage URI format.
Use cases:
- Bundles stored across different storage providers
- Migrating between storage providers (old and new bundles coexist)
- Multi-region deployments with different storage backends
Example with multiple plugins:
export const hotUpdater = createHotUpdater({
database: kyselyAdapter({ db, provider: "sqlite" }),
storages: [
s3Storage({ /* AWS S3 config */ }),
supabaseStorage({ /* Supabase config */ }),
myCustomStorage({ /* My Custom Storage */})
],
basePath: "/hot-updater",
});The server automatically uses the correct plugin to decode each storageUri based on its format.
Route Groups
createHotUpdater exposes two configurable route groups:
updateCheck: update-check endpoints used by React Native clientsbundles: bundle management endpoints used by the CLIstandaloneRepositoryplugin
updateCheck is mounted by default. bundles is disabled by default and must
be enabled explicitly when the CLI needs bundle-management APIs. When
bundles: true, protect /api/bundles* with framework middleware. The
/version endpoint is always mounted for diagnostics. If you provide routes,
specify both configurable route groups.
export const hotUpdater = createHotUpdater({
database: kyselyAdapter({ db, provider: "sqlite" }),
storages: [s3Storage({ /* storage config */ })],
basePath: "/hot-updater",
routes: {
updateCheck: true,
bundles: true,
},
});If you want to hide bundle management endpoints but keep /version, disable the
bundles group:
export const hotUpdater = createHotUpdater({
database: kyselyAdapter({ db, provider: "sqlite" }),
storages: [s3Storage({ /* storage config */ })],
basePath: "/hot-updater",
routes: {
updateCheck: true,
bundles: false,
},
});Recommendation: Match Your CLI Configuration
For most use cases, use only the storage plugin that matches your CLI's hot-updater.config.ts.
CLI (hot-updater.config.ts):
storage: s3Storage({ ... })Server (src/hotUpdater.ts):
storages: [s3Storage({ ... })]This ensures consistency and simplifies configuration. Only use multiple plugins when you need to support bundles from different storage providers.
Available official plugins:
- s3Storage - AWS S3 and Cloudflare R2
- supabaseStorage - Supabase Storage
- firebaseStorage - Firebase Cloud Storage
Database Adapters
Start by setting up your database adapter. Choose the ORM that fits your project:
- Drizzle - TypeScript ORM with SQL-like syntax
- Prisma - Next-generation ORM with intuitive data modeling
- Kysely - Type-safe SQL query builder
- MongoDB - Native MongoDB driver
- S3 - S3-compatible object storage for bundle metadata
Server Frameworks
After configuring your database, choose the framework that fits your stack:
- Hono - Lightweight Web Standard framework
- Express - Popular Node.js framework
- Elysia - Modern Bun-first framework
Key Features
- Framework Agnostic: Use Hono, Express, Elysia, or any Web Standard framework
- ORM choice: Drizzle, Prisma, Kysely, or native MongoDB driver
- Database control: PostgreSQL, SQLite, or MongoDB on your infrastructure
- Cloud storage: Leverage existing AWS S3, Cloudflare R2, Supabase, or Firebase storage
- Type safety: Full TypeScript support across all adapters
- Production ready: Built-in graceful shutdown and error handling