Один файл описує весь ресурс.
З оголошення моделі генеруються таблиця, TypeScript-тип запису, валідатор і скелети маршрутів. Поле змінюється один раз — оновлюється все, що з ним пов’язане.
Файлова маршрутизація. Типізовані моделі. Згенерований CRUD.
bun add -g @hopak/cli Чому Hopak?
З оголошення моделі генеруються таблиця, TypeScript-тип запису, валідатор і скелети маршрутів. Поле змінюється один раз — оновлюється все, що з ним пов’язане.
Без сканера декораторів, DI-контейнера, маршрутів, що з’являються невідомо звідки. Сервер виконує саме те, що лежить у app/. Відкрийте будь-який файл і перепишіть під себе.
SQLite вбудовано у Bun. Перехід на Postgres або MySQL — однією командою hopak use <dialect>. Код моделей лишається без змін.
Дві команди — і Ви маєте проєкт із шістьма REST-ендпоінтами. Валідація, JSON, пагінація, приховування чутливих полів — усе вже всередині, нічого збирати докупи не доведеться.
JWT, OAuth, RBAC у @hopak/auth. Типізовані up / down міграції. Dev-сертифікати через hopak generate cert. Не доведеться добирати й сполучати чотири окремі пакети.
bun:sqlite, Bun.password, Bun.serve, Bun.write — фреймворк використовує те, що Bun уже надає, без Node-обгорток.
Як це працює
Поля, обмеження, звʼязки.
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'),
}); Декларація створює колонки таблиці, TypeScript-тип рядка і валідатор.
Два файли. Шість ендпоінтів.
import { crud } from '@hopak/core';
import post from '../../models/post';
export const GET = crud.list(post);
export const POST = crud.create(post); Згенеровані файли — звичайний TypeScript. Ендпоінт можна змінити або видалити у будь-який момент.
Запит до запущеного сервера.
{
"items": [
{ "id": 1, "title": "Hello", "published": true, "author_id": 4 }
],
"total": 1,
"limit": 20,
"offset": 0
} Сервер валідує вхідні дані, приховує чутливі поля і пагінує списки.