Документація / Моделі

Моделі

Один файл визначає таблицю, тип рядка та валідатор.

Модель — це один файл. Він визначає таблицю, валідацію, TypeScript-тип рядка і (опційно) REST-ендпоїнти.

// app/models/post.ts
import { model, text, boolean, belongsTo } from '@hopak/core';

export default model('post', {
  title: text().required().min(3).max(200),
  content: text().required(),
  published: boolean().default(false),
  author: belongsTo('user'),
});

Типи полів

ТипПримітки
text()Довільний рядок (обмежуйте через .min/.max/.pattern)
email()Рядок із валідацією email-формату
url()Рядок із валідацією URL-формату
phone()Рядок — вбудованого regex немає; для суворих форматів додайте .pattern(...)
number(), money()Числа з min/max (money зберігається як real)
boolean()Скаляр
date(), timestamp()Приводяться з ISO-рядків; відхиляють некоректні дати
enumOf('a', 'b')Літеральний union у TypeScript, enum у БД
json<T>()Типізована JSON-колонка
belongsTo('user'), hasOne('profile'), hasMany('post')Зв’язки
password(), secret(), token()Автоматично виключаються з JSON-відповідей
file(), image()Зберігаються як JSON-метадані { url, mimeType, size, name? }

Модифікатори

Ланцюжок на будь-якому полі:

text().required().min(3).max(200).unique().index()
number().required().min(0).max(100).default(0)
text().pattern(/^[a-z]+$/)
date().default('now')
file().maxSize('5MB')
image().maxSize(2_097_152)   // у байтах також приймається

Параметри

model('post', { /* fields */ }, {
  timestamps: true,   // add createdAt + updatedAt columns (default: true)
});

Хуки життєвого циклу

Додано у 1.0

Хуки виконуються навколо однорядкових записів, звідки б ті не приходили — з CRUD-ендпоїнтів чи Ваших власних викликів ctx.db.model(...). Хуки before* можуть повернути замінений payload; хуки after* призначені для побічних ефектів:

// app/models/user.ts
import { model, text, email, password } from '@hopak/core';

export default model('user', {
  name: text().required(),
  email: email().required().unique(),
  password: password().required().min(8),
}, {
  hooks: {
    async beforeCreate(data) {
      return { ...data, password: await Bun.password.hash(String(data.password)) };
    },
    afterCreate(row) {
      console.log(`user #${row.id} signed up`);
    },
  },
});

Доступні: beforeCreate, afterCreate, beforeUpdate(data, id), afterUpdate, beforeDelete(id), afterDelete(id). Bulk-операції (createMany / updateMany / deleteMany) пропускають хуки — вони транслюються в один SQL-statement.

Хешуєте паролі? Беріть hashPassword з @hopak/auth, а не Bun.password.hash напряму. Він пропускає значення, які вже є хешами, тож хук і credentialsSignup можуть працювати разом без подвійного хешування — подвійний хеш ніколи не збігається під час входу.

Назви таблиць

Додано у 1.0

Фізична таблиця — це назва моделі у множині (postposts), доступна як post.tableName. Вона потрібна щоразу, коли Ви пишете SQL вручну — див. База даних → Назви таблиць у сирому SQL.