логотип PurpleSchool
логотип PurpleSchool

Использование Crystal с Vue для разработки

Автор

Олег Марков

Введение

Современная разработка веб-приложений стремится объединять мощные серверные технологии с удобными и динамичными фронтенд-решениями. Crystal — молодой, производительный язык программирования, вдохновленный синтаксисом Ruby и статической типизацией, отлично подходит для создания высокопроизводительных веб-бэкендов. Vue — популярный JavaScript-фреймворк для создания реактивных интерфейсов.

Когда вы совмещаете Crystal в качестве бэкенда и Vue на фронтенде, вы получаете быстрое, легкое и поддерживаемое приложение, способное выдерживать значительные нагрузки. В этой статье я подробно опишу, как наладить такую связку, какие инструменты и подходы использовать, покажу примеры кода и объясню, как все это работает вместе.

Архитектурные особенности интеграции Crystal и Vue

Почему именно Crystal и Vue?

Crystal — статически типизированный, компилируемый язык, который разрабатывался для достижения практически скорости C с синтаксисом, похожим на Ruby. Это означает, что он понятен для прочтения, легко тестируется и отлично масштабируется для высоконагруженных задач.

Vue — фреймворк с низким порогом входа, модульной архитектурой и возможностью использовать современные фронтенд-подходы, такие как компоненты, реактивность и роутинг.

Связка этих технологий позволяет разделять зоны ответственности: Crystal отвечает за бизнес-логику, API и скорость, а Vue — за динамику пользователя и презентационный слой.

Как строится архитектура приложения

Типичная структура интеграции Crystal и Vue выглядит так:

  • Back-end на Crystal: Серверное приложение, которое обрабатывает бизнес-логику, авторизацию, работу с БД и API-запросы. Обычно строится на web-фреймворке Kemal, Amber или Lucky.
  • Front-end на Vue: SPA-приложение или модульные компоненты, которые общаются с сервером через HTTP или WebSocket.
  • API: Crystal предоставляет REST или GraphQL API, к которому обращается фронтенд.
  • Обработка статики: Сборка Vue-приложения выдается как статические файлы, которые может раздавать сервер Crystal или сторонний HTTP-сервер.

Установка и настройка окружения

Установка Crystal

Для Linux и MacOS:

# Установка через пакетный менеджер
brew install crystal      # для Homebrew (macOS)
sudo apt install crystal  # для Ubuntu (может потребоваться добавить PPA)

Для Windows можно использовать Windows Subsystem for Linux (WSL).

Проверьте корректность установки:

crystal --version
# Должно показать версию установленного Crystal

Инициализация Crystal-проекта

# Создание нового проекта
crystal init app my_api

# Переходим в директорию проекта
cd my_api

Установка и настройка Vue

Сначала проверьте, что у вас установлен Node.js.

node -v
npm -v

Установка Vue CLI:

npm install -g @vue/cli

Создание нового Vue-проекта:

vue create my_frontend
# Интерактивно выберите опции по своему вкусу

Пример работы Crystal API-сервера

Давайте рассмотрим, как создать простое API на Crystal с использованием Kemal.

# В файле src/my_api.cr
require "kemal"

# Простое API, которое возвращает JSON
get "/api/greet/:name" do |env|
  name = env.params.url["name"]
  env.response.content_type = "application/json"
  {message: "Привет, #{name}!"}.to_json
end

Kemal.run
  • Мы создаем эндпоинт /api/greet/:name, который возвращает JSON с приветствием.
  • Сервер запускается с помощью Kemal.run.

Чтобы установить Kemal, добавьте в shard.yml:

dependencies:
  kemal:
    github: kemalcr/kemal

Затем выполните:

shards install
crystal run src/my_api.cr

Настройка CORS для связи с фронтендом

Для разработки, когда фронтенд и бэкенд работают на разных портах, нужен CORS (Cross-Origin Resource Sharing).

Применение CORS в Kemal:

# В файле src/my_api.cr, ДО любых маршрутов
before_all do |env|
  env.response.headers["Access-Control-Allow-Origin"] = "*"
  env.response.headers["Access-Control-Allow-Headers"] = "*"
  env.response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
end
  • Так сервер будет пускать все запросы, в проде обязательно ограничьте Origin.

Настройка взаимодействия с Vue

Интеграция API-запросов

Предположим, вы хотите из компонента Vue отправить GET-запрос к нашему Crystal API.

// Внутри любого .vue компонента или через store (например, Vuex или Pinia)
export default {
  data() {
    return {
      message: ''
    }
  },
  mounted() {
    fetch('http://localhost:3000/api/greet/Мир')
      .then(r => r.json())
      .then(data => {
        this.message = data.message  // Сохраняем приветствие в состояние компонента
      })
  }
}
  • Здесь мы отправляем запрос к API на сервере Crystal и присваиваем ответ в состояние.

Отправка POST-запросов

В Crystal принимаем POST-запрос:

post "/api/data" do |env|
  # Получаем тело запроса (например, JSON)
  body = env.request.body.not_nil!.gets_to_end
  data = JSON.parse(body)
  # work with data: data["name"].as_s, data["value"].as_i, etc.
  env.response.content_type = "application/json"
  {result: "ok"}.to_json
end

Во Vue:

// Пример отправки POST-запроса из Vue
async function sendData() {
  const res = await fetch('http://localhost:3000/api/data', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({name: 'test', value: 123})
  })
  const result = await res.json()
  // Дальнейшая обработка результата
}

Организация передачи данных между бэкендом и фронтендом

Cериализация в Crystal

Для работы с JSON удобно делать структуры и сериализовать их:

require "json"

struct User
  include JSON::Serializable  # Позволяет легко сериализовать и десериализовать структуру

  property id : Int32
  property name : String
end

get "/api/user" do |env|
  user = User.new(id: 1, name: "Иван")
  env.response.content_type = "application/json"
  user.to_json
end
  • Благодаря include JSON::Serializable, Crystal автоматически превращает структуру в JSON.

Стандарт передачи данных

Старайтесь делать API универсальным: используйте camelCase или snake_case везде, где возможно, и обязательно указывайте тип Content-Type.

Во Vue:

async mounted() {
  const res = await fetch('http://localhost:3000/api/user')
  const user = await res.json()
  // Используйте user.id, user.name и т.д.
}

Примеры деплоя и сборки

Сборка и раздача фронтенда статикой через Crystal

После сборки Vue (обычно папка dist):

cd my_frontend
npm run build

В проекте Crystal можно добавить раздачу файлов этой папки:

Kemal.config.public_folder = "../my_frontend/dist"
  • Теперь если пользователь заходит на корень, Crystal будет отдавать index.html, а весь JS/CSS — как статические файлы.

Разделение фронта и бэка на разные серверы

Такой подход удобен в большой системе — фронт-вызовы на отдельный сервер (Nginx, CDN), а API-названия — на Crystal.

  • Для production настраивайте proxy в Nginx: все /api/* — на Crystal, остальное — на папку со сборкой Vue.

Пример конфигурации nginx для Vue+Crystal

server {
    listen 80;
    server_name example.com;

    root /var/www/my_frontend/dist; # Сборка Vue

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
  • Здесь при обращении по /api/* трафик уходит на Crystal API, все остальное обслуживает Vue.

Роутинг и SPA: Особенности

Если ваше Vue-приложение использует HTML5 History Mode (history.pushState), обязательно настройте отдачу index.html на все несуществующие адреса для корректной работы роутера:

  • В Kemal: Kemal.config.public_folder — все неизвестные пути будут отдавать index.html, если его нет по запрошенному адресу.
  • В Nginx — используйте try_files $uri $uri/ /index.html.

Использование WebSocket: Реалтайм-интеграция

Crystal поддерживает WebSocket через Kemal, а во Vue легко использовать стандартные WebSocket API.

Сервер Crystal:

# WebSocket на Crystal с помощью Kemal
ws "/socket" do |socket|
  socket.on_message do |msg|
    # Отправим обратно (echo)
    socket.send "Сервер получил: #{msg}"
  end
end

Клиент во Vue:

created() {
  const socket = new WebSocket("ws://localhost:3000/socket")
  socket.onmessage = (event) => {
    // event.data содержит ответ сервера
    console.log(event.data)
  }
  socket.onopen = () => {
    socket.send('Привет!')
  }
}
  • Так вы легко реализуете чат, уведомления или лайв-обновления данных.

Тестирование и разработка

Разработка с горячей перезагрузкой

  • Crystal Hot Reload: пока в Crystal нет стабильного hot-reload, проще автоматизировать пересборку с помощью сторонних тулов, например, guard-crystal, shotgun.cr или Watchman.
  • Vue: Используйте npm run serve, чтобы иметь live reload фронта.

Отладка ошибок

  • Используйте подробные сообщения об ошибках в Crystal.
  • Для клиентской части Vue пользуйтесь Vue Devtools.

Организация работы с БД

Crystal имеет множество библиотек для работы с PostgreSQL, MySQL, SQLite (например, crystal-pg).

Пример минимального доступа к базе:

require "pg"

DB.open "postgres://user:password@localhost/dbname" do |db|
  db.query "SELECT id, name FROM users" do |rs|
    rs.each do
      puts "#{rs.read(Int32)}, #{rs.read(String)}"
    end
  end
end
  • Интеграция с фронтендом ничем не отличается — ваш API предоставляет данные по запросу, которые Vue подгружает динамически.

Производительность и безопасность

  • Crystal компилируется в нативный код, что увеличивает производительность API.
  • Используйте HTTPS и JWT или другую схему авторизации для production.
  • Взаимодействие через REST или GraphQL позволяет легко строить масштабируемое SPA.

Частозадаваемые технические вопросы и ответы

Как настроить proxy в режиме разработки для запросов с Vue на Crystal API?

Используйте файл vue.config.js в корне проекта Vue, добавьте:

module.exports = {
  devServer: {
    proxy: 'http://localhost:3000'
  }
}

Это позволяет отправлять запросы к /api как будто они идут на тот же сервер, без CORS-проблем.

Как запустить фронтенд и бэкенд одновременно в режиме разработки?

Можно использовать отдельные терминалы (один — crystal run…, второй — npm run serve). Или применить npm-скрипты с пакетами вроде concurrently.

Как подключить Vue к другому домену API на продакшне?

Убедитесь, что сервер API корректно настроил CORS (разрешенные Origins). Для безопасных production-проектов вместо "*" указывайте точный адрес фронта.

Как реализовать аутентификацию (авторизацию) между Crystal и Vue?

Реализуйте аутентификацию по токену (к примеру, JWT). Получайте токен после логина, храните его на фронте (например, localStorage), передавайте его в заголовках запроса.

Почему статические файлы Vue иногда не обновляются после деплоя?

Чаще всего это связано с кешированием браузера. Добавьте хэши к экспортируемым файлам в vue.config.js (filenameHashing: true, используется по умолчанию). Также настройте короткий Cache-Control на сервере для статики, чтобы исключить устаревание.


Эта статья подробно раскрыла тему совместного применения Crystal и Vue, описала архитектуру, настройку, интеграцию, деплой, тестирование и решение типовых задач при такой разработке.

Стрелочка влевоГлубокое изучение документации Vue и как эффективно её использоватьИспользование вычисляемых свойств для динамического отображения данных на Vue jsСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний