Skip to content

Database: Getting Started ​

Holo-JS's database layer is built around configurable runtime setup, typed model definitions, and a fluent query surface.

Typical flow ​

Most applications follow this path:

  1. configure a connection in config/database.ts
  2. write migrations in server/db/migrations
  3. run npx holo migrate
  4. let Holo-JS refresh the internal generated schema metadata under .holo-js/generated
  5. define models under server/models
  6. query through DB.table(...) or the ORM

Database config ​

Put database setup in config/database.ts.

ts
import { defineDatabaseConfig } from '@holo-js/db'
import { env } from '@holo-js/config'
export default defineDatabaseConfig({
  defaultConnection: 'main',
  connections: {
    main: {
      driver: 'sqlite',
      url: './storage/database.sqlite',
    },
    analytics: {
      driver: 'postgres',
      url: env('ANALYTICS_DATABASE_URL'),
    },
  },
})

Use SQLite for local development or switch to MySQL and Postgres by changing the named connection definitions.

Install every concrete driver referenced by the configuration:

bash
bun add @holo-js/db-sqlite
# or: bun add @holo-js/db-postgres
# or: bun add @holo-js/db-mysql

When constructing an adapter directly, import it from its concrete package:

ts
import { createPostgresAdapter } from '@holo-js/db-postgres'

const adapter = createPostgresAdapter({
  connectionString: process.env.DATABASE_URL,
})

The same application-level query and model code should stay stable while the configured database driver changes underneath it.

CLI workflow ​

For day-to-day database work, use the Holo-JS CLI:

bash
npx holo list
npx holo migrate
npx holo migrate:fresh --seed
npx holo seed

npx holo list shows internal database commands plus app commands auto-discovered from server/commands.

Multiple connections ​

One application can use more than one connection.

ts
const events = await DB.table('audit_events', 'analytics').latest().get()

Models can target a named connection with connectionName.

Defining models ​

ts
import { defineModel } from '@holo-js/db'

export const User = defineModel('users', {
  fillable: ['name', 'email'],
})

Use the query builder directly for reporting, maintenance, and table-shaped responses. Use models when the query belongs to one domain record type.

Transactions ​

Transactions are explicit and context-scoped:

ts
await DB.transaction(async (tx) => {
  await tx.table('users').insert({
    name: 'Ava',
    email: '[email protected]',
  })
})

Continue ​

Holo owns backend runtime concerns. The host framework owns SSR and routing.