Гид по технологиям

Отслеживание просмотров страниц в Next.js с помощью Supabase

6 min read Development Обновлено 26 Dec 2025
Отслеживание просмотров с Supabase в Next.js
Отслеживание просмотров с Supabase в Next.js

График на экране ноутбука

Просмотры страниц — простой и полезный показатель, который показывает, какие материалы читают чаще всего. В этом руководстве мы не используем Google Analytics или другие внешние скрипты: всё хранится в вашей базе данных Supabase (Postgres) и доступно в реальном времени.

Что потребуется

  • Проект Next.js (подойдёт Markdown‑базовый блог или стандартный starter). Если блог отсутствует, создайте один, например используя react-markdown, или клонируйте официальный шаблон Next.js с GitHub.
  • Учётная запись на Supabase (бесплатный план подходит для разработки).

Ниже — подробные шаги, код и рекомендации по безопасности, тестированию и расширению аналитики.

Подготовка проекта Next.js

  1. Убедитесь, что у вас есть Next.js проект.
  2. Добавьте в корень проекта файл .env.local, туда позже поместите ключи Supabase.

Важно: не храните секретные ключи в публичных репозиториях. Используйте переменные окружения.

Создание базы данных в Supabase

  1. Перейдите на сайт Supabase, войдите в аккаунт или зарегистрируйтесь.
  2. На панели управления создайте новый проект (кнопка локализована как “Новый проект”).

Снимок экрана панели управления Supabase

  1. Задайте имя проекта и пароль, затем нажмите “Создать новый проект”.

Снимок экрана с настройками проекта Supabase

  1. В настройках проекта в разделе API скопируйте URL проекта и публичный/секретный ключи.

Снимок экрана с ключами API проекта Supabase

  1. Сохраните их в .env.local:
NEXT_PUBLIC_SUPABASE_URL=""
NEXT_PUBLIC_SUPABASE_KEY=""
SUPABASE_SERVICE_KEY=""

(Заполните значения, скопированные из панели Supabase.)

Создание таблицы для просмотров

Откройте SQL-редактор в Supabase и выполните команду для создания таблицы views:

CREATE TABLE views (
  id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  slug text UNIQUE NOT NULL,
  view_count bigint DEFAULT 1 NOT NULL,
  updated_at timestamp DEFAULT NOW() NOT NULL
);

Вы также можете создать таблицу через визуальный редактор таблиц на левой панели.

Supabase table columns

Создание SQL-функции для обновления просмотров

Postgres поддерживает функции, которые можно вызвать через Supabase RPC. Создайте функцию update_views, которая либо увеличит счётчик на 1, либо добавит запись с count = 1.

  1. В SQL-редакторе нажмите “Новый запрос”.
  2. Вставьте и выполните следующий SQL:
CREATE OR REPLACE FUNCTION update_views(page_slug TEXT)
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
  IF EXISTS (SELECT FROM views WHERE slug = page_slug) THEN
    UPDATE views
    SET view_count = view_count + 1,
        updated_at = now()
    WHERE slug = page_slug;
  ELSE
    INSERT INTO views(slug) VALUES (page_slug);
  END IF;
END;
$$;
  1. Нажмите “Run” для выполнения.

Эта функция гарантирует атомарное обновление счётчика на стороне базы данных.

Настройка клиента Supabase в Next.js

Установка зависимости

Установите официальный клиент Supabase:

npm install @supabase/supabase-js

Инициализация клиента

Создайте файл /lib/supabase.ts (или /lib/supabase/admin.ts для серверной части) и инициализируйте клиент:

import { createClient } from "@supabase/supabase-js";

const supabaseUrl: string = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
const supabaseServerKey: string = process.env.SUPABASE_SERVICE_KEY || "";

const supabase = createClient(supabaseUrl, supabaseServerKey);

export { supabase };

Примечание: для клиентского кода используйте публичный ключ NEXT_PUBLIC_SUPABASE_KEY; сервисный ключ SUPABASE_SERVICE_KEY храните исключительно на сервере (API‑маршруты, Edge Functions).

API-маршрут для чтения и записи просмотров

Создайте динамический API-маршрут в pages/api/views/[slug].ts. Он будет обрабатывать POST (увеличение счётчика) и GET (получение общего количества).

import { supabase } from "../../../lib/supabase/admin";
import { NextApiRequest, NextApiResponse } from "next";

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  if (req.method === "POST") {
    await supabase.rpc("update_views", {
      page_slug: req.query.slug,
    });
    return res.status(200).json({ message: "Success" });
  }

  if (req.method === "GET") {
    const { data } = await supabase
      .from("views")
      .select("view_count")
      .filter("slug", "eq", req.query.slug);

    if (data) {
      return res.status(200).json({ total: data[0]?.view_count || 0 });
    }
  }

  return res.status(400).json({ message: "Invalid request" });
};

export default handler;

Обратите внимание: здесь мы вызываем серверный клиент с сервисным ключом, поэтому файл импорта должен ссылаться на серверную инициализацию (admin).

Отображение просмотров в компоненте

Установите SWR для удобного кэширования и реактивного обновления:

npm install swr

Создайте компонент viewsCounter.tsx:

import useSWR from "swr";

interface Props {
  slug: string;
}

const fetcher = async (input: RequestInfo) => {
  const res: Response = await fetch(input);
  return await res.json();
};

const ViewsCounter = ({ slug }: Props) => {
  const { data } = useSWR(`/api/views/${slug}`, fetcher);
  return {`${data?.total ? data.total : "0"} views`};
};

export default ViewsCounter;

Добавьте вызов POST при рендере страницы, чтобы регистрировать просмотры:

import { useEffect } from "react";
import ViewsCounter from "../../components/viewsCounter";

interface Props {
  slug: string;
}

const Post = ({ slug }: Props) => {
  useEffect(() => {
    fetch(`/api/views/${slug}`, { method: "POST" });
  }, [slug]);

  return (
    
{/* содержимое блога */}
); }; export default Post;

Теперь при каждом посещении поста отправляется POST-запрос, который вызывает SQL-функцию и увеличивает счётчик.

Как расширить аналитику

Просмотры — базовый показатель. Подумайте о следующих расширениях:

  • Сохранять referrer (источник перехода) и user agent в отдельной таблице для анализа трафика.
  • Добавлять timestamp событий и хранить срезы для построения графиков.
  • Делать агрегированные запросы в Supabase для дэшборда (например, просмотры за 7/30 дней).
  • Создать материализованные представления для быстрых агрегатов.

Всегда оценивайте конфиденциальность и объём данных — хранение всей информации о пользователях увеличивает расходы и требования по безопасности.

Безопасность и приватность

  • SUPABASE_SERVICE_KEY держите только на сервере. Никогда не инжектируйте его в клиентский бандл.
  • Если храните IP, user agent или другие персональные данные, убедитесь в соответствии с локальными законами о защите данных (GDPR для ЕС). Минимизируйте хранимые персональные данные.
  • Ограничьте публичный доступ к таблицам: используйте политики Row Level Security (RLS) при необходимости.

Тестирование и критерии приёмки

Критерии приёмки:

  • При GET /api/views/[slug] возвращается { total: number } для существующих и 0 для несуществующих slug.
  • При POST /api/views/[slug] счётчик увеличивается на 1 при последовательных запросах.
  • При одновременных POST запросах база данных не теряет инкременты (функция update_views должна работать атомарно).

Тесты:

  • Интеграционный тест: выполняйте POST и затем GET, проверяйте ожидаемый инкремент.
  • Нагрузочный тест (локально или staging): симулируйте десятки/сотни параллельных POST и проверьте консистентность счётчиков.

Роли и чеклист внедрения

Для команды внедрения:

  • Разработчик фронтенда
    • Добавить компонент отображения просмотров
    • Добавить вызов POST в useEffect
  • Бэкенд/инфра
    • Создать таблицу views
    • Создать функцию update_views
    • Настроить серверный клиент Supabase с сервисным ключом
  • DevOps/Безопасность
    • Поместить секреты в защищённые переменные окружения
    • Настроить RLS и правила доступа при необходимости
  • QA
    • Проверить GET/POST API
    • Провести нагрузочное тестирование

Решение задач: когда этот подход НЕ подойдёт

  • Если вам нужна сложная пользовательская аналитика (сессии, воронки, ретенции), лучше использовать специализированные аналитические сервисы.
  • Если проект генерирует очень большой объём событий (миллионы записей), подумайте о потоковой обработке (Kafka, BigQuery) и агрегации для снижения нагрузки на OLTP базу.

Альтернативные подходы

  • Встраиваемая аналитика (Google Analytics, Plausible, Fathom) — быстро, с готовыми отчётами.
  • Edge Functions / Serverless: инкрементировать счётчик через edge‑функции для снижения задержки и нагрузки на главный API.
  • Event queue: писать события в очередь и агрегировать фоновой задачей, чтобы избежать блокировок таблиц при высоком трафике.

Мини‑методология внедрения

  1. Создать таблицу и функцию в отдельной ветке/проекта.
  2. Настроить серверный клиент и тестовый API‑маршрут.
  3. Добавить фронтенд‑компонент и интеграционные тесты.
  4. Запустить на staging, провести нагрузочные тесты.
  5. Деплой в production и мониторинг.

Decision flowchart

flowchart TD
  A[Нужны простые просмотры?] -->|Да| B[Использовать Supabase таблицу + RPC]
  A -->|Нет| C[Выбрать аналитический сервис]
  B --> D{Ожидается высокий трафик}
  D -->|Да| E[Использовать очередь и агрегацию]
  D -->|Нет| F[Прямая запись в RPC]

Краткий словарь

  • Slug — уникальный идентификатор статьи в URL (например, “how-to-supabase”).
  • RPC — удалённый вызов процедур; в Supabase это вызов SQL-функции.
  • RLS — Row Level Security, политики доступа на уровне строк в Postgres.

Локальные отличия и советы

  • Для проектов, ориентированных на Россию/ЕАЭС, проверьте требования локального законодательства по хранению персональных данных. Если вы собираете персональные данные, рассмотрите локальное хранение или согласие пользователя.
  • В локализованных интерфейсах Supabase кнопки могут быть на английском; в описании операций используйте понятные русские обозначения (“Новый проект”, “Запрос SQL”).

Пример сценария миграции с Google Analytics

  1. Для основных отчётов оставьте GA включённым параллельно на 1–2 недели.
  2. Сравните данные просмотров по обеим системам, выясните расхождения (бот‑трафик, сессии).
  3. Отключите GA после подтверждения корректности локальной системы.

Краткие рекомендации по производительности

  • Добавьте индекс по полю slug для ускорения поиска.
  • Если счётчики растут быстро, рассмотрите периодическое агрегирование в другую таблицу и обрезку истории.
  • Используйте транзакции в сложных сценариях, где одновременно меняются несколько таблиц.

Краткое резюме

  • Вы научились: создавать таблицу views, писать SQL-функцию update_views, настраивать клиент Supabase, создавать API-маршрут и фронтенд‑компонент с SWR.
  • Этот подход даёт контроль над данными и гибкость, но требует ответственности за безопасность и масштабирование.

Краткое резюме в виде пунктов:

  • Сохраняйте секретные ключи только на сервере.
  • Тестируйте атомарность обновлений и параллельные запросы.
  • Планируйте расширения аналитики и соблюдайте требования приватности.

Спасибо — теперь вы можете встроить простой и прозрачный счётчик просмотров на базе Supabase в свой Next.js блог.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как поставить скрытую копию (BCC) в Outlook
Email

Как поставить скрытую копию (BCC) в Outlook

Руководство по Apple Pay Cash: настройка и использование
Гайды

Руководство по Apple Pay Cash: настройка и использование

Проверка состояния батареи MacBook
Оборудование

Проверка состояния батареи MacBook

Блокировка отвлекающих сайтов — вернуть фокус
Продуктивность

Блокировка отвлекающих сайтов — вернуть фокус

Режим «Кино» на iPhone — как снимать и монтировать
Фото и Видео

Режим «Кино» на iPhone — как снимать и монтировать

Настройка оформления Kodi — шаг за шагом
Kodi

Настройка оформления Kodi — шаг за шагом