Отслеживание просмотров страниц с помощью Supabase в Next.js
Коротко: этот материал показывает, как с помощью Supabase и Next.js отслеживать просмотры страниц без сторонних аналитических скриптов — создаём таблицу views, хранимую функцию update_views, API-маршрут и компонент счётчика просмотров. Включены варианты, чеклисты, советы по безопасности и примеры тестов.

Page views — важная метрика для оценки производительности сайта и интереса к материалам. Вместо сторонних скриптов (Google Analytics, Fathom) можно использовать собственную базу данных. Supabase — открытая альтернатива Firebase, предоставляющая Postgres, auth, instant API, Edge Functions и real-time подписки — подходит для подсчёта просмотров в реальном времени.
Что вы узнаете в этом руководстве
- Как подготовить Next.js-проект к сбору просмотров.
- Как создать таблицу views и хранимую функцию update_views в Supabase.
- Как настроить клиент Supabase в Next.js и реализовать API-роут для увеличения/чтения счётчика.
- Как подключить компонент ViewsCounter к страницам и регистрировать просмотры.
- Дальше: расширение данных, безопасность, тесты и возможные альтернативы.
Подготовка сайта для отслеживания просмотров
Чтобы повторить шаги, у вас должен быть проект на Next.js. Если блога ещё нет, можно создать Markdown‑блог с react-markdown или клонировать официальный стартовый шаблон Next.js с GitHub.
Важно: подготовьте файл .env.local в корне проекта и не коммитьте в репозиторий секретные ключи.
Создаём базу данных Supabase
- Перейдите на сайт Supabase и войдите или зарегистрируйтесь.
- На панели управления нажмите кнопку “New Project” и выберите организацию (обычно создаётся автоматически под вашим именем пользователя).

- Заполните имя проекта и пароль, затем нажмите кнопку “Create new project”.

- В разделе Settings → API скопируйте Project URL и публичный/секретный ключи (service key).

- Сохраните их в файл .env.local (локально):
NEXT_PUBLIC_SUPABASE_URL=""
NEXT_PUBLIC_SUPABASE_KEY=""
SUPABASE_SERVICE_KEY=""- Перейдите в SQL editor и создайте таблицу views — можно выполнить SQL-запрос или воспользоваться table editor.

Пример SQL для создания таблицы:
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
);Или создайте те же поля вручную через графический редактор таблиц:

Создаём хранимую функцию (Stored Procedure) для обновления просмотров
Postgres поддерживает SQL‑функции, которые удобно вызывать через Supabase RPC. Ниже — функция update_views, увеличивающая счётчик или создающая запись, если её нет.
- В SQL editor нажмите New Query.
- Вставьте и выполните запрос:
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 (или Ctrl/Cmd+Enter).
Пояснение: функция принимает slug публикации и увеличивает view_count на 1 или создаёт запись с дефолтным значением 1.
Важно: не давайте публичный service key в клиентском коде — используйте его на сервере (API-роуты, Edge Functions) или ограничивайте привилегии.
Настраиваем Supabase-клиент в Next.js
Установка пакета
В корне проекта установите официальный клиент:
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.
Реализация API-роута для чтения и регистрации просмотров
Создайте файл /pages/api/views/[slug].ts (или в новой структуре /app/api) — он будет динамическим и получит slug из параметров.
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;Пояснения:
- POST вызывает RPC update_views и увеличивает счётчик (делайте это с клиента при загрузке страницы).
- GET возвращает текущее значение view_count для указанного slug.
- Важно: обрабатывать ошибки и rate-limit (см. раздел «Безопасность и надёжность»).
Компонент счётчика просмотров (ViewsCounter)
Установите swr для удобного получения данных с автоматическим обновлением:
npm install swrСоздайте компонент components/viewsCounter.tsx:
import useSWR from 'swr';
interface Props {
slug: string;
}
const fetcher = async (input: RequestInfo) => {
const res: Response = await fetch(input as string);
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;Этот компонент запрашивает текущее число просмотров и отображает его, пока swr обновляет данные в фоне.
Регистрируем просмотр при открытии страницы
В компоненте, который рендерит пост, вызовите POST к API при первой отрисовке:
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 (
// blog content
);
};
export default Post;Проверьте панель Supabase — значение поля view_count должно увеличиваться при посещении страницы.
Когда этого подхода достаточно и когда он не подходит
- Подходит когда: вы хотите минимальную собственную аналитику без внешних скриптов, нуждаетесь в контроле данных, хотите расширять структуру (рефереры, UTM, уникальные пользователи).
- Не подходит когда: нужны сложные сегменты, отчёты по событиям, анонимизация по регламенту — в таких случаях удобнее готовые аналитические решения или смешанный подход.
Альтернативные подходы
- Использовать готовые аналитические сервисы (Google Analytics, Plausible, Fathom) — быстро, но внешние скрипты.
- Логировать просмотры на стороне CDN/серверов логов и агрегировать в ETL-пайплайне.
- Хранить события просмотров в таблице events (аналог event sourcing) для гибкой аналитики.
Модель принятия решения (умозрительная)
- Нужна ли вам полнота данных и сегменты? Если да — используйте аналитический инструмент.
- Нужен ли полный контроль и гибкость? Если да — Supabase/Postgres подойдёт.
- Требуется ли низкая задержка и real-time? Supabase поддерживает real-time подписки.
flowchart TD
A[Начало] --> B{Нужна аналитика}
B -->|Минимальная| C[Supabase: таблица views]
B -->|Продвинутая| D[GA/Fathom/Plausible]
C --> E{Требуется сегментация}
E -->|Да| F[Добавить таблицу events и UTM]
E -->|Нет| G[Оставить views и dashboard]Факто-бокс: ключевые элементы
- Хранилище: Supabase (Postgres).
- Таблица: views {id, slug, view_count, updated_at}.
- RPC: update_views(page_slug TEXT).
- API: /api/views/[slug] — GET (получить), POST (увеличить).
- Клиент: swr для чтения, fetch POST для регистрации.
Чеклист ролей
Developer:
- Создать таблицу views.
- Написать функцию update_views.
- Настроить API-роут и компонент ViewsCounter.
- Написать тесты на API.
DevOps / SRE:
- Защитить service key, настроить секреты в CI.
- Настроить бэкап базы данных.
- Настроить rate-limits и мониторинг ошибок.
Product / PM:
- Определить, какие метрики нужны (просмотры, рефералы, UTM).
- Решить период хранения и правила удаления данных.
Безопасность и приватность (GDPR и права доступа)
Важно:
- Не храните в счётчике персональные данные (PII) без явного основания.
- Если собираете IP или реферер, укажите это в политике конфиденциальности и при необходимости обеспечьте согласие пользователей.
- Храните service key в защищённых секретах (Vercel/Netlify/CI) и не публикуйте его в клиентском коде.
Тесты и критерии приёмки
Критерии приёмки:
- POST /api/views/:slug увеличивает view_count на 1.
- GET /api/views/:slug возвращает корректное значение total.
- Повторные запросы от одного клиента корректно увеличивают счётчик (поведение определяется вами).
- Секреты не присутствуют в публичном репозитории.
Минимальные тест-кейсы:
- Создать запись через POST и проверить view_count == 1.
- Повторить POST и проверить view_count == 2.
- GET на несуществующий slug возвращает total: 0.
- Неподдерживаемый метод возвращает 400.
Отладка и распространённые проблемы
- Ничего не меняется в базе: проверьте, использует ли API правильный service key и подключение к корректному проекту Supabase.
- CORS и ошибки сети: убедитесь, что запросы с клиентской части идут на ваш API-роут, а не напрямую в Supabase приватной частью.
- Конкурентность: если ожидается очень большой трафик, подумайте о атомарных операциях в базе или использовании инкрементов на уровне SQL.
Шаблон расширения: отслеживание реферера и UTM
Если хотите хранить источник трафика, создайте отдельную таблицу referrers или расширьте events:
- Таблица referrers: id, slug, referrer text, utm_source text, utm_medium text, created_at timestamp.
- Каждый POST может посылать дополнительные поля (referrer, utm) на сервер, где вы сначала валидируете данные, затем вставляете запись и вызываете update_views.
Когда не стоит изобретать велосипед
Если вам нужны готовые панели, сегментация, экспорт данных в BigQuery и сложная аналитика — используйте готовые аналитические решения. Собственная реализация хороша, когда важен контроль и минимальный набор метрик.
Краткое резюме
- Supabase + Next.js дают простой путь для собственной системы подсчёта просмотров.
- Архитектурно: таблица views + RPC update_views + API-роут + клиентский компонент.
- Дополнительно продумайте безопасность ключей, приватность пользователей и тестирование.
Важно: начните с минимальной рабочей реализации, затем расширяйте (рефереры, UTM, события) по мере потребностей.
Глоссарий (одна строка):
- slug — уникальный короткий идентификатор страницы, обычно в URL.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone