Работа с Node.js

Для работы с Universal Router в среде Node.js необходимо сначала установить пакет через npm:

npm install universal-router

После установки можно импортировать библиотеку в проект:

const UniversalRouter = require('universal-router');

Создание маршрутизатора начинается с определения массива маршрутов. Каждый маршрут — это объект с обязательным полем path и функцией action, которая вызывается при совпадении URL:

const routes = [
  {
    path: '/',
    action: () => 'Главная страница'
  },
  {
    path: '/about',
    action: () => 'О нас'
  }
];

const router = new UniversalRouter(routes);

Асинхронная обработка маршрутов

Функция action может быть асинхронной, что особенно удобно при работе с базой данных или внешними API:

const routes = [
  {
    path: '/users/:id',
    async action({ params }) {
      const user = await getUserFromDB(params.id);
      if (!user) return 'Пользователь не найден';
      return `Имя пользователя: ${user.name}`;
    }
  }
];

Маршрутизатор корректно обрабатывает промисы и возвращает результат после их выполнения.

Параметры маршрутов и их использование

Universal Router поддерживает динамические сегменты в пути, которые определяются с помощью двоеточия :. Эти параметры доступны внутри объекта params:

const routes = [
  {
    path: '/posts/:postId/comments/:commentId',
    action({ params }) {
      return `Пост: ${params.postId}, Комментарий: ${params.commentId}`;
    }
  }
];

Можно использовать регулярные выражения для более точного контроля параметров:

const routes = [
  {
    path: '/product/:id(\\d+)',
    action({ params }) {
      return `Продукт с ID: ${params.id}`;
    }
  }
];

Вложенные маршруты

Для сложных приложений часто требуется вложенность маршрутов. Это реализуется через свойство children:

const routes = [
  {
    path: '/dashboard',
    action: () => 'Панель управления',
    children: [
      {
        path: '/stats',
        action: () => 'Статистика'
      },
      {
        path: '/settings',
        action: () => 'Настройки'
      }
    ]
  }
];

При запросе /dashboard/stats маршрутизатор автоматически спустится по дереву маршрутов и вызовет соответствующий action.

Обработка ошибок и fallback маршруты

Universal Router предоставляет возможность задать маршруты по умолчанию и обработку ошибок через специальный маршрут с path: '(.*)':

const routes = [
  {
    path: '/home',
    action: () => 'Главная'
  },
  {
    path: '(.*)',
    action: () => 'Страница не найдена'
  }
];

Ошибки внутри action можно ловить через обычный try/catch или обрабатывать глобально при вызове router.resolve():

router.resolve('/unknown-path')
  .then(result => console.log(result))
  .catch(err => console.error('Ошибка маршрута:', err));

Контекст маршрутов

Каждый маршрут может использовать объект context, который передается в action. Это полезно для передачи глобальных зависимостей, настроек или сервисов:

const router = new UniversalRouter(routes, {
  context: {
    db: databaseInstance,
    logger: console
  }
});

const routes = [
  {
    path: '/users/:id',
    async action({ params, context }) {
      const user = await context.db.getUser(params.id);
      context.logger.log(`Запрос пользователя ${params.id}`);
      return user;
    }
  }
];

Генерация URL и обратное сопоставление

Universal Router не предоставляет встроенной функции генерации URL, но совместим с библиотеками вроде path-to-regexp, что позволяет создавать маршруты динамически и строить ссылки по шаблону:

const { compile } = require('path-to-regexp');

const toPath = compile('/posts/:id');
console.log(toPath({ id: 42 })); // /posts/42

Интеграция с сервером Node.js

Маршрутизатор легко интегрируется с любым HTTP-сервером, например с http модулем Node.js:

const http = require('http');

http.createServer(async (req, res) => {
  try {
    const result = await router.resolve(req.url);
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end(result);
  } catch (err) {
    res.writeHead(500, { 'Content-Type': 'text/plain' });
    res.end('Внутренняя ошибка сервера');
  }
}).listen(3000);

Это позволяет строить полноценные серверные приложения с маршрутизацией без использования тяжелых фреймворков.

Поддержка Middleware

Для обработки промежуточных действий перед вызовом action можно использовать кастомный middleware-подход, оборачивая router.resolve:

async function loggingMiddleware(url) {
  console.log('Запрос к URL:', url);
  return router.resolve(url);
}

loggingMiddleware('/users/1').then(console.log);

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

Настройка приоритетов маршрутов

Порядок маршрутов имеет значение: первый совпавший маршрут обрабатывается первым. Поэтому маршруты с более специфичными путями следует размещать выше, а общие fallback маршруты — внизу массива.