npm package: @ortha/server-content-manager
Description: CMS content-type definitions, a schema factory, and a Fastify plugin that registers content types on the server instance. Converts declarative content-type definitions into Sequelize models with automatic base columns, metadata columns, and index generati
Latest version: 0.0.4

# @ortha/server-content-manager

CMS content-type definitions, a schema factory, and a Fastify plugin that registers content types on the server instance. Converts declarative content-type definitions into Sequelize models with automatic base columns, metadata columns, and index generation.

## Installation

Internal monorepo dependency — import directly:

```ts
import {
    contentManagerServerPlugin,
    extractDatabaseSchemas
} from '@ortha/server-content-manager';
import type { ContentTypeDefinition } from '@ortha/server-content-manager';
```

## Usage

### Defining content types

```ts
import { DataTypes } from 'sequelize';
import type { ContentTypeDefinition } from '@ortha/server-content-manager';

const articleType: ContentTypeDefinition = {
    slug: 'article',
    metadata: {
        type: 'collection',
        modelName: 'Article',
        tableName: 'articles',
        paranoid: true,
        publishable: true,
        localizable: false
    },
    schema: {
        label: 'Article',
        description: 'Blog articles'
    },
    fields: [
        {
            name: 'title',
            column: { type: DataTypes.STRING, allowNull: false },
            field: { label: 'Title' }
        },
        {
            name: 'body',
            column: { type: DataTypes.TEXT, allowNull: true },
            field: { label: 'Body' }
        }
    ]
};
```

### Registering with the database and server

```ts
import { bootstrap } from '@ortha/server-platform-bootstrap';
import { databasePlugin } from '@ortha/server-platform-database';
import {
    extractDatabaseSchemas,
    contentManagerServerPlugin
} from '@ortha/server-content-manager';

const contentTypes = [articleType];

await bootstrap({
    config: bootstrapConfig,
    plugins: [
        databasePlugin({
            ...dbConfig,
            schemas: [identitySchema, extractDatabaseSchemas(contentTypes)]
        }),
        contentManagerServerPlugin({ contentTypes })
    ]
});

// Access at runtime: fastify.contentManager.contentTypes
```

## API Reference

### Functions

| Export                         | Kind     | Description                                                        |
| ------------------------------ | -------- | ------------------------------------------------------------------ |
| `extractDatabaseSchemas()`     | function | Converts `ContentTypeDefinition[]` to Sequelize `SchemaDefinition` |
| `contentManagerServerPlugin()` | function | Fastify plugin — decorates `fastify.contentManager`                |

### Types

| Export                       | Kind | Description                                                |
| ---------------------------- | ---- | ---------------------------------------------------------- |
| `ContentTypeDefinition`      | type | Top-level content-type schema (slug, metadata, fields)     |
| `ContentTypeField`           | type | Single field definition (name, column, field, localizable) |
| `ContentTypeFieldProps`      | type | Admin UI field properties (label)                          |
| `ContentTypeIndex`           | type | Index definition (fields, unique, name)                    |
| `ContentTypeMetadata`        | type | Classification + Sequelize model options                   |
| `ContentTypeSchemaProps`     | type | Schema-level UI properties (label, description)            |
| `ContentManagerRegistry`     | type | Runtime registry shape (`{ contentTypes }`)                |
| `ContentManagerPluginConfig` | type | Plugin config (`{ contentTypes }`)                         |

## Metadata Options

| Property      | Type                     | Default  | Description                                           |
| ------------- | ------------------------ | -------- | ----------------------------------------------------- |
| `type`        | `'collection' \| 'page'` | required | Content-type classification                           |
| `modelName`   | `string`                 | required | PascalCase Sequelize model name                       |
| `tableName`   | `string`                 | required | snake_case database table name                        |
| `paranoid`    | `boolean`                | `false`  | Enable soft deletes (adds `deletedAt`)                |
| `publishable` | `boolean`                | `false`  | Enable publication workflow (adds `publishedAt`)      |
| `localizable` | `boolean`                | `false`  | Enable localisation (adds `locale` + `localeGroupId`) |
| `indexes`     | `ContentTypeIndex[]`     | —        | Composite or custom indexes                           |

## Auto-Generated Columns

Every content type automatically receives:

- **Base columns**: `id` (UUID primary key), `projectId` (foreign key to projects), `createdAt`, `updatedAt`
- **Conditional columns** (based on metadata):
    - `paranoid: true` → `deletedAt` (soft delete)
    - `publishable: true` → `publishedAt` (nullable DATE)
    - `localizable: true` → `locale` (required STRING) + `localeGroupId` (UUID)

## Internal Structure

```
src/lib/
├── types/index.ts                              # All type definitions + Fastify module augmentation
├── extractDatabaseSchemas/index.ts             # ContentTypeDefinition[] → SchemaDefinition
├── baseColumns/index.ts                        # Base columns: id, projectId, timestamps
├── metadataColumns/index.ts                    # Conditional columns: deletedAt, locale, publishedAt
├── modelOptions/index.ts                       # Sequelize model options: tableName, paranoid, indexes
└── contentManagerServerPlugin/index.ts         # Fastify plugin: decorates fastify.contentManager
```

## Key Patterns

- `extractDatabaseSchemas()` returns a frozen `SchemaDefinition` matching the database plugin lifecycle.
- Column construction is split into `baseColumns()`, `metadataColumns()`, and user-defined fields.
- Field `column` values are native Sequelize `ModelAttributeColumnOptions` — passed directly to `sequelize.define()`.
- `contentManagerServerPlugin()` decorates `fastify.contentManager` with a frozen registry for runtime access.
- Individual fields can opt in to localisation via `field.localizable: true`.

## Dependencies

- `@ortha/server-platform-database` — `SchemaDefinition` type
- `fastify-plugin` — Fastify plugin wrapper
- `sequelize` — ORM (peer dependency)

## Building

```bash
nx build server-content-manager
```