Architecture & Delivery
SupabaseJWTAuthenticationProduct ArchitectureRisk Patterns

Як великий JSON у Supabase user_metadata може зламати застосунок

Сучасні продукти рідко гинуть від одного катастрофічного рішення. Частіше — від ланцюга «нешкідливих» скорочень, які тихо ростуть, доки все не трісне.

`user_metadata` у Supabase — приклад такої пастки: корисний на старті, небезпечний у масштабі й дуже зручний для неправильного використання.

Команди часто сприймають його як випадкове місце для даних користувача. Це ж просто JSON? Так, але з наслідком: Supabase включає metadata у JWT сесії. У описаному cookie-сценарії цей токен живе в cookies й подорожує з запитами. Більше metadata — більші запити.

Це не патерн зберігання. Це патерн навантаження. І він має архітектурні наслідки.

Meme about guys sitting still and then cheering

Прихований механізм

Шлях виглядає так:

1. Зберігаєте дані в `auth.users.user_metadata`.

2. Supabase відображає metadata у JWT.

3. JWT зберігається в cookies.

4. Cookies їдуть із запитами.

5. Увесь delivery-конвеєр перевозить персональні дані, яких йому не треба.

Працює, доки не перестає.

Кілька полів здаються невинними. Додайте поведінку, стан UI, налаштування каталогів, аналіз профілю — і запити ростуть, заголовки пухнуть, дебаг стає непослідовним, а автентифікація — світлофором із коротким замиканням.

Зрештою щось голосно ламається. Типові симптоми:

•Застосунок без попередження викидає користувачів.
•Браузер відхиляє запити.
•Сервер повертає HTTP 431: Request Header Fields Too Large.

Тут уже неважливо, який чистий код. Шар автентифікації став вузьким місцем.

Коли це стало реальним

Я не теоретизую. Сам урізався в цю стіну.

Два повні вихідні з’ясовував, чому вхід раптом ненадійний. Без великих деплоїв і складних релізів. Лише дрібні доповнення в `user_metadata`: рядок тут, стан інтерфейсу там.

Нічого, що виглядало небезпечним. Усе, що ним було.

Не база. Не інфраструктура. Навіть не продуктивність у звичному сенсі. JWT.

Токен мав бути ключем, а став валізою з JSON, яку тягали з кожним запитом. І причина не в мегабайтах, а в кількох десятках рядків metadata, що непомітно перейшли межу здорового глузду.

Не драматичний злам. Повільне удушення.

Принцип, якого не вчать

Сесія ≠ сховище

JWT — не склад, не кеш і не контейнер для мінливого контексту продукту.

Токен сесії має бути малим, передбачуваним, стабільним і нудним. Для решти є інші місця.

Знати, хто користувач, — нормально. Носити його вподобання, аналітику, UI, останні дії й половину roadmap — це пакувати продукт у паспорт. А паспорт не для перевезення меблів.

Правильний патерн

У `user_metadata` залишайте те, що вміститься на візитівці. Решту — в окремі таблиці.

Моє правило: контекст ідентичності — `user_metadata`, решта — власні таблиці. Змінюється з часом — не кладіть у сесію. Здається зручним — перепитайте себе. Не впевнені — ізолюйте.

Ширший погляд

Це не стільки про Supabase, скільки про різницю між продуктом, який масштабується, і продуктом, який накопичує ентропію.

Малі помилки проявляються не одразу. Вони виходять саме тоді, коли продукт набирає попит — у найгірший момент для сюрпризів.

Сесія вже важча, ніж мала б? Логи входу — загадки? Metadata працює більше за базу? Ви на початку відмови, якої можна уникнути. Хороша новина: це виправляється.

Висновок

Успішні продукти не тонуть у власних даних. Вони їх структурують.

Автентифікація — рукостискання, не вантажна доставка. Якщо JWT несе metadata замість ідентичності, ви не масштабуєтеся — буксуєте. І це можна виправити до того, як стане дорого.

Поговорімо про ясність.

Менше балачок — більше прибутку. Збудуємо реальну систему!

Пряма інженерія на рівні коду для операцій, які не можуть дозволити собі збій чи затримку