Как собрать свой URL-shortener на Firebase: пошаговый опыт

Короткие ссылки нужны не только для твитера. Если вы делаете лендинг с UTM-метками, шерите ссылки в рассылках или просто хотите контролировать, куда ведёт каждый переход — свой короткий сервис удобнее, чем сторонние сервисы с рекламой и лимитами. Firebase позволяет собрать полноценный URL-shortener за один вечер, без серверов и с бесплатным тарифом, которого хватает с головой для большинства задач.

Что мы собираем и почему именно Firebase

Итоговый сервис будет работать так: вы отправляете длинный URL — получаете короткий код, который перенаправляет на оригинал. Вся логика умещается в три компонента:

  • Firebase Hosting — раздаёт статику и обрабатывает редиректы.
  • Cloud Firestore — хранит соответствие «короткий код → длинный URL».
  • Cloud Functions — генерирует коды и записывает их в базу.

Почему Firebase, а не свой сервер или другой бэкенд:

  • Бесплатный Spark-план даёт 50 000 чтений Firestore в день, 2 млн загрузок с Hosting и 2 млн вызовов Cloud Functions — для личного или небольшого проекта этого более чем достаточно.
  • Не нужно настраивать сервер, сертификаты, домены — всё уже работает внутри экосистемы Google.
  • Редиректы на уровне Hosting работают быстро, потому что отдаются с edge-серверов Google без участия вашего кода.

Что понадобится перед началом

  1. Аккаунт Google и проект в Firebase Console.
  2. Node.js версии 18+ и установленный Firebase CLI (npm install -g firebase-tools).
  3. Базовое понимание JavaScript — писать сложного ничего не нужно, но читать и копировать код придётся.
  4. Домен (опционально) — можно использовать бесплатный поддомен your-project.web.app, но для серьёзного проекта лучше привязать свой.

Шаг 1. Создаём проект и подключаем Hosting

В Firebase Console создаём новый проект, включаем Hosting и инициализируем его локально:

firebase login
firebase init hosting

На вопрос «What do you want to use as your public directory?» отвечаем public. На вопрос о конфигурации как single-page app — отказываемся, если не собираем SPA. После инициализации в корне проекта появится папка public и файл firebase.json.

Шаг 2. Настраиваем Firestore для хранения ссылок

В консоли включаем Cloud Firestore (режим production или test — для начала можно test, потом настроите правила безопасности). Создаём коллекцию links. Каждый документ будет содержать:

  • shortCode — короткий код (например, abc123).
  • longUrl — оригинальный длинный URL.
  • createdAt — дата создания.
  • clicks — счётчик переходов (опционально).

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

Шаг 3. Пишем Cloud Function для создания коротких ссылок

Инициализируем Functions:

firebase init functions

В файле functions/index.js пишем функцию, которая принимает длинный URL, генерирует уникальный код и сохраняет его в Firestore:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

function generateCode(length = 6) {
  const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  let result = '';
  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
}

exports.createShortLink = functions.https.onCall(async (data, context) => {
  const { longUrl } = data;
  if (!longUrl || !longUrl.startsWith('http')) {
    throw new functions.https.HttpsError('invalid-argument', 'Valid URL is required');
  }

  const shortCode = generateCode();
  await db.collection('links').doc(shortCode).set({
    longUrl,
    createdAt: admin.firestore.FieldValue.serverTimestamp(),
    clicks: 0
  });

  return { shortCode };
});

Обратите внимание: мы используем onCall, а не HTTP-триггер. Это удобнее для вызова с фронтенда — Firebase SDK сам обрабатывает авторизацию и сериализацию данных.

Шаг 4. Настраиваем редиректы в firebase.json

Самое интересное — как сделать так, чтобы your-domain.com/abc123 перенаправлял на длинный URL без серверной логики. Для этого в firebase.json добавляем rewrite-правило:

{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "/:shortCode",
        "function": "redirectFunction"
      }
    ]
  }
}

Теперь пишем функцию redirectFunction, которая ищет код в Firestore и делает редирект:

exports.redirectFunction = functions.https.onRequest(async (req, res) => {
  const shortCode = req.path.split('/')[1];
  if (!shortCode) {
    res.status(404).send('Not found');
    return;
  }

  const doc = await db.collection('links').doc(shortCode).get();
  if (!doc.exists) {
    res.status(404).send('Link not found');
    return;
  }

  const { longUrl, clicks } = doc.data();
  await doc.ref.update({ clicks: clicks + 1 });
  res.redirect(302, longUrl);
});

Важный момент: мы используем onRequest, а не onCall, потому что rewrite в Hosting работает именно с HTTP-запросами. Редирект отдаётся на edge-серверах Google, поэтому скорость будет отличной.

Шаг 5. Делаем простой фронтенд

В папке public создаём index.html — минимальную форму для создания ссылок:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>URL Shortener</title>
  <script src="https://www.gstatic.com/firebasejs/9.0.0/firebase-app-compat.js"></script>
  <script src="https://www.gstatic.com/firebasejs/9.0.0/firebase-functions-compat.js"></script>
</head>
<body>
  <input type="text" id="longUrl" placeholder="Вставьте длинный URL">
  <button onclick="createLink()">Сократить</button>
  <p id="result"></p>

  <script>
    const firebaseConfig = { /* ваш конфиг */ };
    firebase.initializeApp(firebaseConfig);
    const functions = firebase.functions();

    const createShortLink = functions.httpsCallable('createShortLink');

    async function createLink() {
      const url = document.getElementById('longUrl').value;
      const { data } = await createShortLink({ longUrl: url });
      document.getElementById('result').textContent =
        `Короткая ссылка: window.location.origin/{data.shortCode}`;
    }
  </script>
</body>
</html>

Это базовая версия. При желании можно добавить копирование в буфер, историю ссылок, QR-коды — но для начала хватит и этого.

Шаг 6. Деплоим и проверяем

firebase deploy

После деплоя вы получите URL вида https://your-project.web.app. Открываете, вставляете длинную ссылку, нажимаете кнопку — получаете короткий код. Переходите по короткой ссылке — попадаете на оригинал. Всё работает.

Правила безопасности Firestore: не забудьте это

По умолчанию в production-режиме Firestore запрещает все операции. Если этого не сделать, любой сможет читать и писать в вашу базу. Минимальные правила для нашего случая:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /links/{shortCode} {
      allow read: if true; // нужно для редиректов
      allow write: if false; // запись только через Cloud Functions
    }
  }
}

Запрет на запись для клиентов — ключевой момент. Все новые ссылки должны создаваться только через Cloud Function, где мы контролируем валидацию и генерацию кода.

Сравнение подходов: когда Firebase подходит, а когда нет

Критерий Firebase URL-shortener Свой сервер (Node.js + PostgreSQL) Готовый сервис (Bitly, Rebrandly)
Сложность запуска Низкая — за вечер Средняя — нужен сервер, деплой, мониторинг Минимальная — регистрация и API
Стоимость при малых объёмах Бесплатно От $5/мес за VPS Бесплатно с ограничениями
Контроль над данными Полный Полный Частичный
Кастомизация редиректов Ограничена функциями Firebase Любая Зависит от тарифа
Масштаб выше 100 000 переходов/день Нужно следить за лимитами Горизонтальное масштабирование Тарифы с оплатой за объём
Аналитика кликов Нужно дорабатывать Любая Встроенная

Что выбрать в зависимости от вашей ситуации

Если вам нужен личный короткий сервис для проекта с небольшим трафиком — Firebase идеален. Бесплатно, быстро, без головной боли с инфраструктурой. Хватит на годы.

Если вы делаете продукт для клиентов с аналитикой, брендированными доменами и высокими нагрузками — начните с Firebase для MVP, но планируйте миграцию на собственный бэкенд, когда перестанут хватать лимиты или понадобится гибкость.

Если вам критична встроенная аналитика, командный доступ и SLA — используйте Bitly или Rebrandly. Не изобретайте велосипед, если это не ваш основной продукт.

Частые ошибки и как их избежать

  1. Коллизия коротких кодов. Случайная генерация не гарантирует уникальность. Решение: перед записью проверяйте, существует ли документ с таким кодом, и если да — генерируйте новый. Либо используйте последовательные ID с префиксом, но это менее эстетично.
  2. Отсутствие валидации URL. Если не проверять входные данные, можно получить пустые ссылки, javascript: протоколы или ссылки на фишинговые сайты. Всегда проверяйте формат на уровне Cloud Function.
  3. Забыли про правила Firestore. Открытая база — это мусорная свалка через неделю. Любой бот начнёт писать туда что угодно.
  4. Нет обработки ошибок на фронтенде. Если Cloud Function упадёт, пользователь должен увидеть понятное сообщение, а не молча ждать.
  5. Использование test-режима Firestore в продакшене. Он автоматически закрывается через 30 дней. Переключитесь на production и настройте правила до того, как сервис начнёт работать по-настоящему.

Практические рекомендации

  • Длина кода. 6 символов из алфавита a-zA-Z0-9 дают около 56 миллиардов комбинаций. Для личного проекта этого хватит с запасом. Если генерируете миллионы ссылок — увеличьте до 8 символов.
  • Кэширование редиректов. Добавьте заголовок Cache-Control: public, max-age=300 в ответ redirectFunction. Это снизит нагрузку на Firestore для популярных ссылок.
  • Логирование. Используйте functions.logger.info() для записи каждого создания ссылки. Это поможет отследить аномалии, если что-то пойдёт не так.
  • Резервное копирование. Настройте экспорт Firestore через firestore:export по расписанию. Потерять базу ссылок — значит сломать все короткие ссылки, которые вы когда-либо разослали.
  • Мониторинг квот. В Firebase Console есть вкладка Usage & billing. Добавьте уведомления о приближении к лимитам, чтобы не обнаружить внезапно неработающий сервис.

Что можно улучшить, если базовая версия заработала

Когда ядро работает, хочется добавить полезных вещей:

  • QR-код для каждой короткой ссылки — генерируется на фронтенде за две строки с любой библиотекой.
  • Пользовательские алиасы — возможность задать свой код вместо случайного, с проверкой на занятость.
  • Истечение ссылок — добавьте поле expiresAt и проверяйте его в функции редиректа.
  • Статистика переходов — записывайте каждый переход в отдельную коллекцию с временем, user-agent и IP (с учётом политик конфиденциальности).
  • Защита от спама — ограничьте количество создаваемых ссылок с одного IP через rate limiting на уровне Cloud Functions.

Итог

Firebase даёт всё необходимое для запуска собственного URL-shortener без серверов, без абонентской платы и с минимальным объёмом кода. Базовая версия — Hosting, Firestore и две Cloud Functions — покрывает 90% задач. Главное — не забыть про правила безопасности, валидацию входных данных и обработку ошибок. Если проект вырастет из личного инструмента в продукт — архитектура позволит постепенно наращивать функциональность без переписывания с нуля.

Начните с простой формы и редиректа. Запустите за один вечер. Если доживёте до момента, когда Firebase начнёт ограничивать вас — значит, проект вырос, и это уже другая, гораздо более приятная проблема.

Dfncfg.ru