Когда в 2021 году Wasp представили как full-stack фреймворк с собственным языком для описания логики, это казалось смелым ходом. Но через три года команда признала: кастомный DSL стал проблемой. Вот как выглядел типичный сценарий:
// Старый синтаксис Wasp DSL
app todoApp {
title: "ToDo App",
entities: [Task],
pages: [Main],
operations: {
getAllTasks: {
fn: import { getAll } from "@server/tasks.js"
}
}
}
На практике такой подход создавал больше трений, чем преимуществ. Вместо ускорения разработки приходилось постоянно переключаться между ментальными моделями — TypeScript для клиента/сервера и Wasp DSL для конфигурации. Проблема усугублялась тем, что:
- IDE не понимали кастомный синтаксис
- Сообщество не могло расширять язык
- Интеграция с существующими TypeScript-тулзами требовала костылей
Почему TypeScript — не очевидный выбор
Переход на TypeScript кажется логичным только постфактум. На самом деле были варианты:
- JSON Schema — слишком ограничен для логики
- YAML с плагинами — теряем type safety
- Парсинг TS как AST — сложность поддержки
Команда Wasp выбрала третий путь, но с хитрой модификацией: они не парсят TypeScript, а используют его как runtime. Вот как теперь выглядит спецификация:
// Новый синтаксис на TypeScript
import { Task } from "@wasp/entities"
import { getAllTasks } from "@server/tasks.js"
const todoApp = defineApp({
title: "ToDo App",
entities: [Task],
pages: {
Main: {
component: import("./MainPage"),
getInitialData: () => getAllTasks()
}
},
operations: {
getAllTasks: {
resolver: getAllTasks
}
}
})
Ключевые изменения:
- Полная поддержка TypeScript-инструментов (автодополнение, refactoring)
- Импорты работают как в обычном коде
- Динамическая загрузка через
import()вместо строковых путей
Что это даёт на практике
В процессе тестирования нового подхода я выделил три неочевидных преимущества:
1. Композиция через обычный код Раньше для reuse логики между проектами приходилось копипастить YAML-подобные блоки. Теперь можно выносить части конфига в отдельные функции:
// shared/configs/auth.ts
export const withAuth = () => ({
auth: {
method: 'email',
onAuthFailedRedirectTo: '/login'
}
})
// my-app/app.ts
import { withAuth } from '@my-lib/configs'
const app = defineApp({
...withAuth(),
// остальная конфигурация
})
2. Динамическая генерация спецификации Поскольку это обычный TS, можно генерировать части конфига программно:
// Генерация CRUD-эндпоинтов для всех моделей
const crudOperations = Object.values(entities).map(entity => ({
[`getAll${entity.name}s`]: {
resolver: () => prisma[entity.name].findMany()
}
}))
3. Расширяемость через декларации типов Сообщество может добавлять свои DSL-конструкты без модификации ядра:
// @types/wasp.d.ts
declare module '@wasp/spec' {
interface AppConfig {
featureFlags?: Record<string, boolean>
}
}
// Использование
defineApp({
featureFlags: { 'newDashboard': true }
})
Где подводные камни
Не всё идеально в новом подходе:
- Производительность сборки — парсинг динамических
import()требует дополнительного шага - Сложность миграции — старые проекты требуют ручного перевода
- Оверхеад для простых проектов — теперь нужно явно указывать типы даже для базовых конфигов
Особенно болезненным оказался пункт про производительность. На проекте с 50+ entity время сборки увеличилось с 1.2s до 2.8s — существенно для HMR-циклов.
Кому это реально полезно
Из моего опыта, новый Wasp особенно хорош для:
- Команд, которые уже используют TypeScript — минимальный порог входа
- Full-stack разработчиков — меньше контекстных переключений
- Библиотек поверх Wasp — теперь можно делать плагины на TS
При этом я бы не рекомендовал его для:
- Простых MVP, где достаточно Next.js + REST
- Команд без TS-экспертизы
- Проектов с кастомными non-JS-интеграциями
Что попробовать дальше, если заинтересовало:
- Поиграть с примером аутентификации
- Проверить, как работает генерация кода через
wasp compile - Попробовать расширить типы через
@types/wasp
Лично мне больше всего нравится, как Wasp теперь использует TypeScript не как цель, а как средство. Это не “ещё один фреймворк”, а скорее набор правил, как организовывать full-stack логику. И после недели использования кажется, что они на правильном пути — даже если придётся ещё попотеть над перфомансом.
Источник: https://wasp.sh/blog/2026/06/15/wasp-typescript-spec