Интеграция с базами данных

Hyperapp — минималистичный фронтенд-фреймворк для построения реактивных веб-приложений. Несмотря на свою компактность, Hyperapp легко интегрируется с различными источниками данных, включая REST API, GraphQL и прямые подключения к базам данных через сервер. Работа с базами данных в Hyperapp подразумевает использование действий (actions) и эффектов (effects) для асинхронного взаимодействия с сервером.


Архитектура взаимодействия

Основной поток данных в Hyperapp строится по принципу:

state → view → actions → effects → state

  1. State — глобальное состояние приложения, которое хранит данные, полученные из базы данных.
  2. View — функция, создающая виртуальный DOM на основе текущего состояния.
  3. Actions — функции, изменяющие состояние или инициирующие эффекты.
  4. Effects — побочные эффекты, включая запросы к базе данных или серверу.
  5. Mutation — результат асинхронного запроса обновляет state.

Такой подход позволяет отделить логику получения данных от их отображения, что делает приложение чистым и предсказуемым.


Настройка state для данных из базы

Для работы с базой данных в state обычно создаются отдельные поля, соответствующие типам данных:

const state = {
  users: [],
  loading: false,
  error: null
};
  • users хранит массив объектов, полученных из базы.
  • loading используется для отображения индикатора загрузки.
  • error хранит информацию о возможных ошибках при запросе.

Определение actions для запросов

Actions отвечают за вызов эффектов и обновление состояния:

const actions = {
  fetchUsers: () => (state, actions) => {
    return {
      loading: true,
      error: null,
      users: []
    };
  },
  setUsers: (users) => (state) => ({ users, loading: false }),
  setError: (error) => (state) => ({ error, loading: false })
};
  • fetchUsers подготавливает состояние к загрузке.
  • setUsers обновляет данные после успешного запроса.
  • setError фиксирует ошибки.

Эффекты для асинхронного взаимодействия

Эффекты выполняются отдельно от действий и могут быть подключены через сторонние API или fetch-запросы:

const effects = {
  getUsers: async (props, actions) => {
    try {
      const response = await fetch('/api/users');
      const data = await response.json();
      actions.setUsers(data);
    } catch (err) {
      actions.setError(err.message);
    }
  }
};
  • Асинхронные эффекты позволяют не блокировать интерфейс.
  • Ошибки обрабатываются через отдельные actions.

Пример полной интеграции

Объединение state, actions и effects:

import { app } FROM "hyperapp";

const state = {
  users: [],
  loading: false,
  error: null
};

const actions = {
  fetchUsers: () => (state, actions) => {
    actions.getUsers();
    return { loading: true, error: null };
  },
  setUsers: (users) => (state) => ({ users, loading: false }),
  setError: (error) => (state) => ({ error, loading: false }),
  getUsers: async (props, actions) => {
    try {
      const response = await fetch('/api/users');
      const data = await response.json();
      actions.setUsers(data);
    } catch (err) {
      actions.setError(err.message);
    }
  }
};

const view = (state, actions) => (
  <div>
    {state.loading && <p>Загрузка данных...</p>}
    {state.error && <p style={{color: "red"}}>{state.error}</p>}
    <ul>
      {state.users.map(user => <li>{user.name}</li>)}
    </ul>
    <button oncl ick={actions.fetchUsers}>Загрузить пользователей</button>
  </div>
);

app({ init: state, view, actions });

Особенности:

  • Асинхронная функция getUsers вызывается через action fetchUsers.
  • Отдельное поле loading помогает управлять индикатором загрузки.
  • Ошибки обрабатываются централизованно через setError.

Интеграция с различными типами баз данных

  1. REST API: основной способ получения данных. Используются стандартные fetch-запросы и JSON.
  2. GraphQL: позволяет запрашивать только необходимые поля, интегрируется через fetch или Apollo Client.
  3. WebSocket и Real-time DB: для живого обновления данных можно использовать эффекты, подписанные на события.
  4. Прямое подключение к серверу: через Node.js сервер, который обрабатывает SQL/NoSQL запросы и возвращает JSON.

Принцип одинаков: Hyperapp взаимодействует только с конечной точкой (endpoint), а сервер уже занимается непосредственной работой с базой данных.


Работа с пагинацией и фильтрацией

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

const state = {
  users: [],
  page: 1,
  LIMIT: 10,
  filter: '',
  loading: false,
  error: null
};

Actions изменяют фильтры или страницу:

const actions = {
  setPage: (page) => (state, actions) => {
    actions.fetchUsers({ page });
    return { page };
  },
  setFilter: (filter) => (state, actions) => {
    actions.fetchUsers({ filter });
    return { filter };
  }
};

Эффект getUsers использует эти параметры для формирования запроса к серверу.


Оптимизация и кеширование

  • Использование localStorage или IndexedDB для кеширования данных.
  • Обновление только изменившихся объектов, чтобы минимизировать перерисовку.
  • Эффекты можно комбинировать с debounce для фильтров и поиска.

Заключение по архитектуре

Hyperapp, будучи минималистичным фреймворком, не ограничивает способы интеграции с базой данных. Структура state → actions → effects → state позволяет создавать масштабируемые, асинхронные приложения с чистой архитектурой. Важным является правильное разделение ответственности: actions изменяют состояние, эффекты выполняют асинхронные операции, а view отвечает за визуализацию.