Skip to content

Database

Viseed CMS uses PostgreSQL with Drizzle ORM for type-safe database access. The connection is managed via Bun's built-in SQL driver (bun:sql), so no separate PostgreSQL client package is needed.

PostgreSQL only

Only PostgreSQL is supported. Other databases (MySQL, SQLite, etc.) are not compatible.


Connection

Provide your connection string via DATABASE_URL or directly in the config:

typescript
const cms = createCMS({
  db: {
    driver: 'postgres',
    url: process.env.DATABASE_URL ?? 'postgresql://localhost:5432/hana',
  },
})

Connection string format:

postgresql://[user]:[password]@[host]:[port]/[database]

For managed databases with SSL (e.g. DigitalOcean, Supabase):

postgresql://user:pass@host:5432/db?sslmode=verify-full&sslrootcert=./ca-certificate.crt

Schema Management

Use the viseed db commands to manage your database schema. The CLI automatically discovers the core schema plus schemas from all installed viseed-plugin-* packages.

Development

Push your current schema directly to the database. This is the fastest way to iterate during development — no migration files are generated.

bash
bunx viseed db push

WARNING

db push may drop columns or alter tables to match the schema. Do not use in production.

Production

Generate SQL migration files that can be reviewed and committed to source control:

bash
bunx viseed db generate

Apply pending migrations to the database:

bash
bunx viseed db migrate

The recommended production workflow:

bash
# 1. Generate migration files (in CI or locally)
bunx viseed db generate

# 2. Review generated files in drizzle/
git add drizzle/
git commit -m "feat: add migration"

# 3. Apply migrations on the server
bunx viseed db migrate

Core Tables

The following tables are created by Viseed CMS core:

TableDescription
sitesSite definitions (supports multi-site)
site_domainsDomain mapping per site
usersUser accounts
user_site_rolesRole assignments per user per site
sessionsAuth sessions
installed_pluginsPlugin registry (enabled plugins per site)
installed_themesTheme registry (installed themes per site)
theme_stateActive theme state per site
media_filesUploaded media files

Plugin tables are prefixed by the plugin name, e.g. blog_posts, blog_categories, menu_items.


Schema Merging

Plugins contribute their own Drizzle table definitions. All plugin schemas are merged with the core schema at startup:

typescript
// Internally handled by createCMS — you don't call this directly
const finalSchema = mergeSchemas(coreSchema, ...pluginSchemas)

When running viseed db commands, the CLI scans your package.json for viseed-plugin-* dependencies and auto-generates a schema barrel file to feed into drizzle-kit.


Custom Schemas

If your application defines additional Drizzle tables beyond what plugins provide, register them in viseed.config.ts at the project root:

typescript
// viseed.config.ts
export default {
  extraSchemas: ['./src/schema.ts'],
}

These will be included when running any viseed db command.

Example custom schema:

typescript
// src/schema.ts
import { pgTable, text, timestamp } from 'drizzle-orm/pg-core'

export const mySchema = {
  subscriptions: pgTable('subscriptions', {
    id: text('id').primaryKey(),
    email: text('email').notNull(),
    createdAt: timestamp('created_at').defaultNow(),
  }),
}