Deploy on Node
A CovaraApp runs standalone on Node via startServer from covara/node (which wraps @hono/node-server).
import { createClient } from "@libsql/client";
import { drizzle } from "drizzle-orm/libsql";
import { createCovara } from "covara";
import { startServer } from "covara/node";
import { todos } from "./schema.js";
import { env } from "./env.js"; // your createEnv schema — see Environment variables
const db = drizzle(createClient({ url: env.DB_FILE_NAME }));
const app = createCovara({ cors: true }).resource(todos, {
db, id: todos.id, auth: { public: true },
});
const server = await startServer(app, {
port: env.PORT,
hostname: "0.0.0.0",
onListen: ({ port }) => console.log(`Listening on ${port}`),
});
startServer(app, options) resolves once the server is listening and returns { port, address, close }. If port is omitted, PORT from the environment is used (default 3000).
Graceful shutdown
By default startServer installs SIGTERM/SIGINT handlers that drain the instance before exiting. On a shutdown signal it:
- Flips readiness so
/readyzreturns503— load balancers and Kubernetes readiness probes stop routing new traffic here. - Closes long-lived SSE subscriptions cleanly so clients reconnect to a healthy instance instead of seeing a dropped socket.
- Waits a bounded drain window (
drainTimeoutMs, default 10s) before closing the listener and exiting.
await startServer(app, {
port: 3000,
drainTimeoutMs: 15000, // drain window before forcing the socket closed
// gracefulShutdown: false, // opt out and manage shutdown yourself via close()
});
Set gracefulShutdown: false to manage shutdown yourself — the returned close() still performs the same drain sequence. /healthz (liveness) keeps returning 200 during drain; only /readyz flips to 503.
Docker
npx covara create writes a Dockerfile and docker-compose.yml (app + Redis, plus Postgres when --db postgres) and a .dockerignore. See the CLI.