@rawdash/hono
v0.29.2
Published
Hono adapter for rawdash — router factories and helpers that mount @rawdash/server handlers onto a Hono app. Runtime-agnostic (Workers, Node, Bun, Deno).
Readme
@rawdash/hono
Hono adapter for rawdash. Mounts @rawdash/server handlers as Hono routers — runtime-agnostic, so it works in Cloudflare Workers, Node, Bun, Deno, or anywhere Hono runs.
What it is
A thin wrapper around @rawdash/server's pure handlers. Each factory returns a Hono app you mount with app.route('/path', router). You provide:
getConfig(c)andgetStorage(c)— per-request functions that the adapter calls to obtain theDashboardConfigandServerStoragefor the request. Return constants for the simple case, or derive values fromc(path params, auth headers, env bindings) when each request needs its own config or storage.beforemiddleware — typically auth and authorization checks.
No business logic lives here — every router delegates to a @rawdash/server handler and translates RawdashError into structured HTTP responses.
Install
npm install @rawdash/hono honoHono is a peer dependency.
Quick start
The mountEngine helper builds a fully-wired Hono app for the simple case (one config, one storage):
import { createClient } from '@libsql/client';
import { LibsqlStorage } from '@rawdash/adapter-libsql';
import { mountEngine } from '@rawdash/hono';
const storage = new LibsqlStorage({
client: createClient({ url: 'file:rawdash.db' }),
});
const { app } = mountEngine(config, { storage });
// Cloudflare Worker / Bun / Deno: export the app
export default app;
// Node: use @hono/node-server
// import { serve } from '@hono/node-server';
// serve({ fetch: app.fetch, port: 8080 });This mounts:
| Path | Method | Source |
| ---------------------------------------- | ------ | ----------------------- |
| /health | GET | createHealthRouter |
| /sync/state | GET | createSyncStateRouter |
| /sync | POST | createSyncRouter |
| /dashboards/:dashboardId/widgets[/...] | GET | createWidgetsRouter |
| /retention/retain | POST | createRetentionRouter |
mountEngine also starts a background retention loop on long-lived runtimes; pass { startRetention: false } on serverless and trigger retention via your platform's scheduler instead.
Per-request config and storage — compose factories directly
For deployments that need auth or that look up config / storage per request, skip mountEngine and compose the factories. Each factory accepts before middleware that runs before the handler, plus the getConfig / getStorage callbacks:
import {
createHealthRouter,
createSyncRouter,
createSyncStateRouter,
createWidgetsRouter,
} from '@rawdash/hono';
import { Hono } from 'hono';
import { assertScope, requireAuth } from './my-auth';
import { loadConfig, loadStorage } from './my-loaders';
const app = new Hono();
app.route('/health', createHealthRouter()); // public liveness probe
const authedApp = new Hono();
authedApp.use('*', requireAuth);
authedApp.route(
'/dashboards',
createWidgetsRouter({
before: [assertScope('widgets:read')],
getConfig: (c) => loadConfig(c),
getStorage: (c) => loadStorage(c),
}),
);
authedApp.route(
'/sync',
createSyncRouter({
before: [assertScope('widgets:write')],
getConfig: (c) => loadConfig(c),
getStorage: (c) => loadStorage(c),
}),
);
authedApp.route(
'/sync/state',
createSyncStateRouter({
before: [assertScope('widgets:read')],
getStorage: (c) => loadStorage(c),
}),
);
app.route('/', authedApp);
export default app;The pure handlers in @rawdash/server do all the work; this package only translates HTTP. Adapters in other frameworks (Express, NestJS, etc.) would be a parallel thin layer over the same handlers.
Widget cache (optional)
createWidgetsRouter accepts an optional cache: (c: Context) => WidgetCache factory. It is invoked once per request, so the returned cache can be scoped to the request's tenant / auth context:
import { createWidgetsRouter } from '@rawdash/hono';
app.route(
'/dashboards',
createWidgetsRouter({
getConfig: (c) => loadConfig(c),
getStorage: (c) => loadStorage(c),
cache: (c) => new MyKvWidgetCache(c.env, c.get('orgId')),
}),
);See the WidgetCache section in @rawdash/server for the interface and error-isolation semantics. Omit cache and behavior is identical to the no-cache path.
Deferred sync mode (queue-backed runners)
By default createSyncRouter runs the sync in-process: the handler records the queued transition and then kicks off runSync(config, storage) as a background promise that iterates config.connectors.
For deployments where the actual sync work runs out-of-process — typically a queue/worker setup where credentials, retries, and rate-limit budgets live in a separate runtime — pass mode: 'deferred'. The trigger handler then only persists the queued transition; the running → succeeded/failed transitions become the storage's responsibility, driven by the external runner:
authedApp.route(
'/sync',
createSyncRouter({
mode: 'deferred',
before: [assertScope('widgets:write')],
getStorage: (c) => loadStorage(c),
// getConfig can be omitted in deferred mode — useful when you can't
// materialize OSS-format Connector instances at request time.
}),
);In deferred mode:
runSyncis never invoked by the trigger handler.getConfigis optional and never called.markSyncQueuedis called exactly as in in-process mode; its return value drives the{queued: true|false}response.- Your external worker is responsible for calling
markSyncSucceededandmarkSyncFailedon the same storage.markSyncRunningis optional onServerStorageand may be omitted by deferred-mode storages — the running transition is driven by the external runner's own aggregation, not the trigger handler.
Running on Node
@rawdash/hono does not depend on @hono/node-server — the package stays runtime-agnostic so a Workers bundle doesn't pull Node-specific code. To run on Node, add @hono/node-server yourself:
npm install @hono/node-serverimport { serve } from '@hono/node-server';
import { mountEngine } from '@rawdash/hono';
const { app } = mountEngine(config);
serve({ fetch: app.fetch, port: 8080 });Error mapping
Handler errors translate to JSON:
RawdashError→{ error: message, code }aterr.status(e.g. 404{error:'Dashboard not found', code:'DASHBOARD_NOT_FOUND'}).- Other errors propagate to Hono's
onErrorfor you to handle.
Links
- rawdash docs
@rawdash/server— pure handlers + engine (the package this wraps)@rawdash/sdk-client— typed HTTP client (speaks the same wire contract)- GitHub
- Issues
License
Apache-2.0
