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

Просмотры страниц — простой и полезный показатель, который показывает, какие материалы читают чаще всего. В этом руководстве мы не используем Google Analytics или другие внешние скрипты: всё хранится в вашей базе данных Supabase (Postgres) и доступно в реальном времени.
Что потребуется
- Проект Next.js (подойдёт Markdown‑базовый блог или стандартный starter). Если блог отсутствует, создайте один, например используя react-markdown, или клонируйте официальный шаблон Next.js с GitHub.
- Учётная запись на Supabase (бесплатный план подходит для разработки).
Ниже — подробные шаги, код и рекомендации по безопасности, тестированию и расширению аналитики.
Подготовка проекта Next.js
- Убедитесь, что у вас есть Next.js проект.
- Добавьте в корень проекта файл .env.local, туда позже поместите ключи Supabase.
Важно: не храните секретные ключи в публичных репозиториях. Используйте переменные окружения.
Создание базы данных в Supabase
- Перейдите на сайт Supabase, войдите в аккаунт или зарегистрируйтесь.
- На панели управления создайте новый проект (кнопка локализована как “Новый проект”).
- Задайте имя проекта и пароль, затем нажмите “Создать новый проект”.
- В настройках проекта в разделе API скопируйте URL проекта и публичный/секретный ключи.
- Сохраните их в .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
);Вы также можете создать таблицу через визуальный редактор таблиц на левой панели.
Создание SQL-функции для обновления просмотров
Postgres поддерживает функции, которые можно вызвать через Supabase RPC. Создайте функцию update_views, которая либо увеличит счётчик на 1, либо добавит запись с count = 1.
- В SQL-редакторе нажмите “Новый запрос”.
- Вставьте и выполните следующий 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;
$$;- Нажмите “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: писать события в очередь и агрегировать фоновой задачей, чтобы избежать блокировок таблиц при высоком трафике.
Мини‑методология внедрения
- Создать таблицу и функцию в отдельной ветке/проекта.
- Настроить серверный клиент и тестовый API‑маршрут.
- Добавить фронтенд‑компонент и интеграционные тесты.
- Запустить на staging, провести нагрузочные тесты.
- Деплой в 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
- Для основных отчётов оставьте GA включённым параллельно на 1–2 недели.
- Сравните данные просмотров по обеим системам, выясните расхождения (бот‑трафик, сессии).
- Отключите GA после подтверждения корректности локальной системы.
Краткие рекомендации по производительности
- Добавьте индекс по полю slug для ускорения поиска.
- Если счётчики растут быстро, рассмотрите периодическое агрегирование в другую таблицу и обрезку истории.
- Используйте транзакции в сложных сценариях, где одновременно меняются несколько таблиц.
Краткое резюме
- Вы научились: создавать таблицу views, писать SQL-функцию update_views, настраивать клиент Supabase, создавать API-маршрут и фронтенд‑компонент с SWR.
- Этот подход даёт контроль над данными и гибкость, но требует ответственности за безопасность и масштабирование.
Краткое резюме в виде пунктов:
- Сохраняйте секретные ключи только на сервере.
- Тестируйте атомарность обновлений и параллельные запросы.
- Планируйте расширения аналитики и соблюдайте требования приватности.
Спасибо — теперь вы можете встроить простой и прозрачный счётчик просмотров на базе Supabase в свой Next.js блог.
Похожие материалы
Как поставить скрытую копию (BCC) в Outlook
Руководство по Apple Pay Cash: настройка и использование
Проверка состояния батареи MacBook
Блокировка отвлекающих сайтов — вернуть фокус
Режим «Кино» на iPhone — как снимать и монтировать