Skip to main content

Quick Start

There are two ways to get going: scaffold a fresh project with the CLI, or add Covara to an app you already have.

Option 1 — Scaffold with the CLI

The fastest path is the covara create scaffolder:

npx covara create my-app # Node + SQLite (default)
npx covara create my-app --template cloudflare # Cloudflare Workers + D1
npx covara create my-app --db postgres # PostgreSQL
tip
Want a full-stack app? Add --frontend react
npx covara create my-app --frontend react

This is the easiest way to get a complete real-time full-stack application. On top of the API it scaffolds a React + Vite SPA wired to the typed client with useLiveList — so the UI updates in real time out of the box — and a single-process dev server: npm run dev serves the SPA (with HMR), the API (/api), and the admin UI (/__covara) on one origin, while live-regenerating the typed client. No proxy, no second terminal.

FlagValuesDefault
--frontendreact, nonenone
--templatenode, cloudflarenode
--dbsqlite, postgressqlite
--no-installskip dependency install

This generates a complete project: a Drizzle schema, database setup, a drizzle.config.ts, a running server, the right package.json scripts, and — with --frontend react — a ready-to-edit React frontend. See the CLI reference for everything it produces.

cd my-app
npm run dev # start the server — covara dev creates/updates tables automatically

npm run dev runs covara dev, which auto-applies additive schema changes on start (creating your tables on first run), so there's no separate npm run db:push. Use db:push only for destructive changes or in CI.

Option 2 — Add to an existing app

Install

npm install covara drizzle-orm @libsql/client zod

drizzle-orm, zod, and (for the client hooks) react are peer dependencies — install the versions your project uses.

1. Define your schema

// src/schema.ts
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";

export const usersTable = sqliteTable("users", {
id: integer("id").primaryKey({ autoIncrement: true }),
email: text("email").notNull().unique(),
name: text("name").notNull(),
role: text("role").default("user"),
createdAt: integer("createdAt", { mode: "timestamp" }),
});

2. Set up the database

// src/db.ts
import { drizzle } from "drizzle-orm/libsql";
import { createClient } from "@libsql/client";

const client = createClient({ url: "file:./data.db" });
export const db = drizzle(client);

3. Create the API

// src/main.ts
import { createCovara } from "covara";
import { startServer } from "covara/node";
import { usersTable } from "./schema.js";
import { db } from "./db.js";

const app = createCovara({ cors: true }).resource(usersTable, {
id: usersTable.id,
db,
auth: { public: true },
});

const server = await startServer(app, { port: 3000 });
console.log(`Server running on http://localhost:${server.port}`);

That's it. You now have a full REST API:

MethodPathDescription
GET/api/usersList users (paginated, filtered, ordered)
GET/api/users/:idGet a single user
POST/api/usersCreate a user
PATCH/api/users/:idUpdate a user (partial)
PUT/api/users/:idReplace a user
DELETE/api/users/:idDelete a user
GET/api/users/countCount users (filtered)
GET/api/users/aggregateAggregation queries
GET/api/users/aggregate/subscribeLive aggregation (SSE)
GET/api/users/subscribeReal-time subscription (SSE)
GET/api/users/searchFull-text search (when configured)
POST/api/users/batchBatch create
PATCH/api/users/batchBatch update
DELETE/api/users/batchBatch delete
POST/api/users/rpc/:nameRPC procedures

Health endpoints (/healthz, /readyz) and the OpenAPI spec (under /__covara) are mounted by default. See Generated endpoints for the full reference.

The createCovara factory

createCovara(options) returns a CovaraApp (which extends Hono) with sensible defaults: RFC 7807 error handling, health endpoints, OpenAPI generation, and security headers.

const app = createCovara({
basePath: "/api", // resource mount prefix (default: "/api")
cors: true, // or a hono/cors config object
auth: { router, middleware }, // result of useAuth()
middleware: [], // extra Hono middleware applied to all routes
observability: true, // request/subscription metrics
health: true, // /healthz + /readyz (default: enabled)
adminUI: true, // admin dashboard at /__covara (default: disabled)
openapi: true, // OpenAPI spec at /__covara (default: enabled)
})
.resource(usersTable, { id: usersTable.id, db }) // mounts at /api/users
.resource("/people", usersTable, { id: usersTable.id, db }); // custom path

.resource() is chainable and infers the mount path from the table name unless you pass one explicitly. Full option reference: Resources & the app factory.

Using a plain Hono app

useResource returns an ordinary Hono router, so you can compose everything yourself instead of using the factory:

import { Hono } from "hono";
import { useResource, errorHandler, notFoundHandler } from "covara";

const app = new Hono();
app.onError(errorHandler);
app.notFound(notFoundHandler);

app.route("/api/users", useResource(usersTable, { id: usersTable.id, db }));

Connect the client

// client.ts
import { getOrCreateClient } from "covara/client";

export const client = getOrCreateClient({
baseUrl: location.origin,
credentials: "include",
offline: true, // optimistic updates + offline queue
});
// App.tsx
import { useAuth, useLiveList } from "covara/client/react";

function App() {
const { isAuthenticated } = useAuth<User>();
if (!isAuthenticated) return <LoginPage />;
return <UserList />;
}

function UserList() {
const { items } = useLiveList<User>("/api/users", { orderBy: "name:asc" });
return <ul>{items.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}

The list updates in real time as anyone mutates /api/users. Continue with the Client library.

Next steps

  • Tutorial — build a complete real-time todo app, server and client.
  • Resources — every configuration option in depth.
  • Authentication — add login, sessions, scopes.
  • Deployment — ship to Node or Cloudflare Workers.