Как создать сайт на PocketBase и Nuxt3

Как я создавал платформу для внутренних торгов мебелью и почему для этого выбрал PocketBase + Nuxt 3

Иван Демидов
Иван Демидов Фронтенд-разработчик
8 мая 2026

История одного пет-проекта. Как разработчик Selectel создал портал, используя для этого фреймворк Nuxt3 и Pocketbase — open source-решение для хранения данных.

Изображение записи

Привет, Я Ваня Демидов, разработчик Selectel. В этой статье я расскажу о своем опыте использования фреймворка Nuxt3 и Pocketbase — open source-решения для хранения данных.

Задумка и исходники

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

Мем: несколько клетчатых китайских сумок стоят рядом в форме дивана. Подпись: продам дизайнерский диван-трансформер, еще и в синем цвете.

Для начала предстояло оценить фронт работ: собрать данные и продумать механику платформы. Коллеги из других отделов помогли сфотографировать мебель, определить степень ее износа и стоимость. Все данные мы занесли в Excel-таблицу. 

После перешли к самой механике. Нам было важно, чтобы распродажа проходила честно. Если бы мы просто отправили в рабочий чат ссылку на список мебели, то покупателем стал тот, кто первым отреагировал на сообщение. Мы же хотели дать возможность поучаствовать всем коллегам.

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

Почему PocketBase и Nuxt3

Мне стало интересно реализовать такую задумку, поэтому я взялся за разработку сайта.

Если оценить в целом, то платформа напоминает некий интернет-магазин с долей рандома. Но перед тем как приступить к разработке, необходимо понять, какая функциональность требуется и какая технология подойдет лучше.

Какие были требования?

  • Информация о товарах должна где-то лежать — то есть нужна база данных. Также желательно, чтобы у нее была панель администратора для визуализации и простоты восприятия. На тот момент в Selectel работало более 1 000 сотрудников, поэтому большой нагрузки на базу не предвиделось.
  • Чтобы определить личность участника, необходима аутентификация. Важное условие: участвовать может только сотрудник компании, поэтому нужно запретить регистрацию пользователей из внешнего мира. 
  • Для регистрации аккаунта требуется подтверждение по почте — понадобится почтовый сервер.
  • Для отображения интерфейса нужен фреймворк. Желательно что-то простое и знакомое, так как для разработки задействован только один человек — я.

Изначально я хотел для фронта использовать React, а для бэкенда — WordPress, но передумал. У меня нет большой экспертизы в этой библиотеке, да и многочисленные настройки CMS оказались ни к чему. Так что мой выбор пал на Nuxt3 и PocketBase.

С Nuxt3 все просто: он хорошо мне знаком, ведь мы пишем на нем наш сайт. Кроме того, у него есть режим статической генерации страниц — на мой взгляд, это самый подходящий для проекта подход к конструированию страниц. А еще Nuxt3 из коробки предоставляет клиентский роутинг без перезагрузки страницы — современное и эстетичное решение. Ну и вишенка, у него хорошая и подробная документация

А вот PocketBase мне подсказал коллега, когда я рассказал ему про требования к бэкенду. В итоге инструмент хорошо подошел под мои задачи: привлекли простота и наличие всех необходимых фишек. 

Оказалось, что встроенная аутентификация здесь настолько же проста, насколько разнообразна. Здесь можно настроить аутентификацию по паролю, OTP, OAuth2 или выбрать другие варианты. Я остановился на первом: он вполне безопасный и простой.

Так как PB предоставляет готовый способ аутентификации, здесь есть и готовый почтовый сервер, который настраивается за несколько минут. А еще панель администратора выглядит стильно и минималистично — как раз подходит под мои требования:

Скриншот панели PB.

Еще в PocketBase легко создавать реляционные таблицы. При создании появляется интуитивно понятный интерфейс управления таблицей, удобный интерфейс управлением API и готовый Swagger:

Интерфейс управлением API.

Осталось лишь понять, хватит ли ресурса этой БД (а она использует SQLite), чтобы обеспечить непрерывную работу при 1 000 активных пользователях единовременно. Оказалось, что да — PocketBase выдерживает до 10 000 активных пользователей. Нагрузка также зависит от количества операций, но и их будет не так много.

Какие были трудности 

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

Та самая ошибка.

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

На самом деле, у PB есть и другие недостатки. Главный —  эту технологию все еще нельзя использовать в серьезных проектах. О чем, кстати, написано на первой же странице документации. Но этот «временный» минус можно не брать в расчет, если вам нужно быстрое и современное решение для пет-проекта вроде моего.

Когда все таблицы были вновь заведены, можно было приступать к наполнению их данными. Я писал выше, что вся информация о мебели лежит в Excel — это примерно 100 позиций. Переносить вручную такое количество данных — пустая трата времени, гораздо удобнее (и быстрее) было автоматизировать этот процесс.

Какие таблицы в БД понадобились:

  • пользователи — очевидно;
  • продукты — не менее очевидно;
  • запросы — вместо того, чтобы создавать отдельное поле в каждом товаре, например usersId, мне захотелось сделать отдельную таблицу, где были userId + productId. 
Та самая таблица.
Поля для продукта.


Легкий скрипт на Node.js, запущенный локально, перенес заранее сконвертированный JSon со всей «мебельной» информаций в базу — и здесь меня вновь подстерегала незадача. В таблице оказались ссылки на фото мебели, доступ к которым был ограничен.

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

Еще одна проблема появилась в конце разработки, когда я показал приложение коллегам из отдела безопасности. В статье «От идеи до эксплойта» можно почитать об уязвимостях, которые они нашли. Но к слову, здесь мне вообще не стыдно, потому что я уже все пофиксил.

Логика работы сайта

Сам сайт находится за балансировщиками, поэтому единственная возможность увидеть его — зайти из нашей офисной сети. У него есть несколько страниц:

  • общая — со всеми товарами;
  • правила участия в розыгрыше;
  • корзина добавленные в корзину товары больше не показываются в общей ленте);
  • страница входа/регистрации;
  • и самая, на мой взгляд, интересная — карточка продукта. Про нее и поговорим.
Карточка продукта.

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

Карточка продукта — так ее видит администратор.


Если товар добавлен в корзину по ошибке или участник передумал, то его можно удалить.

Сам сайт был статическим, но «товары» на нем динамические. Именно поэтому мне стало интересно реализовать возможность динамического добавления предметов в админку и последующего отображения без необходимости пересборки. На помощь пришли query-параметры, которые я считывал на странице /products/product?id=${productId} при ее монтировании. Где id — это productId, по которому я получал информацию о товаре и запросах других пользователей.

Страница со всеми продуктами выглядит так:

Скриншот страницы продукта, на нем 8 карточек товаров (хотя на самом деле их гораздо больше). У каждой карточки есть фото, название предмета мебели и цена.

Из особенно удобного на ней — фильтрация по категориям. Изначально в документе нет данных о категории, но я добавил очередной скрипт на Node.js — пробежался по массиву данных и в зависимости от слов, встречающихся в названии товара — поставил категорию.

Добавляем еще немного заботы о коллегах в виде хлебных крошек, ссылок на терминологию и другие страницы приложения. Собираем красивую иконку из тирекса и маленькой тележки — и можно в прод.

Итоги

Магазин был успешно запущен и не лег в момент запуска распродажи. Я был счастлив, что никто из коллег не пришел спрашивать, как работает интерфейс. Это значит, с проектированием я тоже справился. И даже регистрация с подтверждением по почте не вызвала негодования, хотя часто мне задавали вопрос, почему не использовал SSO. 

Да, не обошлось без нюансов. Так как у коллег была возможность отказаться от выигранных позиций, часто (раз 10–20) приходилось вручную в базе сбрасывать победителя и разыгрывать товары снова. Считаю, что функция getAnotherWinner была бы очень полезна. Но в остальном сайт работает как нужно — а значит, проект удался.

Мем из фильма «Декстер», полицейский с прищуром наблюдает за происходящим.
Это я смотрю на коллег, которые отказались от всей мебели, и мне пришлось все переигрывать, но ничего не могу им предъявить.