Quickstart

Create Federation

Initialize the Downcity server-side Federation with the default database and HTTP service.

The server-side runtime class is Federation (also called the Federation in these docs); product clients use the City client to reach it. See Federation and City for the distinction.

Install the packages:

pnpm add @downcity/city drizzle-orm better-sqlite3

The database is the only thing you need before boot. On first boot Federation fills DOWNCITY_FEDERATION_ADMIN_SECRET_KEY, DOWNCITY_FEDERATION_TOKEN_SIGNING_KEY, and BETTER_AUTH_SECRET automatically. Provider keys are written later through the Admin API or the fed admin workspace:

OPENAI_API_KEY="..."
OPENAI_BASE_URL="https://api.openai.com/v1"

Create Federation:

import { Federation } from "@downcity/city";
import Database from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";

const sqlite = new Database("./data.sqlite");
sqlite.pragma("journal_mode = WAL");

const db = Object.assign(drizzle(sqlite), {
  $client: { exec: (sql: string) => sqlite.exec(sql) },
});

const base = new Federation({ db });

The default database is stored at:

./data.sqlite

For local SQLite, better-sqlite3 is the recommended path. For production Postgres, create a Drizzle pg db and pass that db to Federation the same way.

For Cloudflare Workers / D1, pass the D1 Drizzle db:

import { drizzle } from "drizzle-orm/d1";

const db = drizzle(env.DB);
const base = new Federation({ db });

Federation initializes itself automatically on the first health(), handleRequest(), table(), or similar runtime call.

If your Federation needs its own business tables, declare them on a Service or service:

import { sqliteTable, text } from "drizzle-orm/sqlite-core";
import { Service } from "@downcity/city";

const notes = sqliteTable("notes", {
  id: text("id").primaryKey(),
  title: text("title").notNull(),
  status: text("status").notNull(),
});

const notesService = new Service({
  id: "notes",
  tables: { notes },
});

base.use(notesService);

const table = await base.table("notes.notes");
await table.insert({
  id: "note_1",
  title: "First note",
  status: "draft",
});

Tables declared by services use the same database connection as Federation. Regular projects do not need manual table creation.

Start the HTTP service:

import { serve } from "@hono/node-server";

await base.health();
serve({ fetch: base.router().fetch, port: 3001, hostname: "127.0.0.1" });

This service is the shared Federation used by later cities. Management and business data are handled through your own database and city backend for now.