Ось твій урок у стилі CS50. Вмикаймо уяву, відкриваймо IDE, і поїхали! 🚀
🎓 Тема: Response Models і контроль відповіді API (або «Не показуй зайвого!»)
Привіт, друзі! Ласкаво просимо на черговий етап нашої подорожі у світ бекенду.
1. 🔥 Вступ: проблема та мотивація
Уявіть, що ви — власник елітного ресторану. 🍽️ Клієнт замовляє вашу фірмову страву. На кухні відбувається хаос: брудний посуд, лушпиння від картоплі, чеки від постачальників, секретні нотатки шеф-кухаря з рецептом соусу.
Питання до вас: Коли офіціант виносить страву клієнту, чи виносить він разом з нею сміттєве відро і той самий секретний рецепт? Звісно, ні! Клієнт отримує лише красиву, чисту тарілку з їжею. Нічого зайвого.
А тепер погляньмо на код.
Ви створюєте API для користувачів. У вашій базі даних є об'єкт User, який містить:
* id
* username
* email
* hashed_password (хеш пароля!)
* created_at
* secret_internal_rating
Якщо ви просто повернете цей об'єкт "як є" на запит фронтенду... Що станеться? Ви віддасте клієнту (і потенційному хакеру) хеш пароля та внутрішні дані. Це катастрофа безпеки! 🚨
Сьогодні ми навчимося бути тим самим ідеальним офіціантом. Ми розберемо Response Models — механізм, який дозволяє чітко контролювати, що саме виходить з вашого API назовні, незалежно від того, що відбувається "на кухні" (у базі даних).
2. 🧠 Теоретична база (без сухої академічності)
Отже, що таке Response Model?
Response Model (Модель відповіді) — це схема-фільтр, яка визначає форму даних, що будуть відправлені клієнту. Вона бере ваші "сирі" дані, валідує їх і відсікає все зайве.
Найчастіше в сучасному Python (наприклад, у FastAPI) для цього використовується бібліотека Pydantic.
Як це працює «під капотом»? ⚙️
Уявіть це як сито або трафарет. 1. Ваша функція дістає з бази даних "товстий" об'єкт з купою полів. 2. Фреймворк бере вашу Response Model (ваш "трафарет"). 3. Він прикладає трафарет до об'єкта. 4. Все, що не влізає у трафарет — ігнорується. 5. Все, що влізає — перетворюється у JSON і летить до клієнта.
Що треба запам'ятати:
- Input ≠ Output. Модель для входу (створення юзера) і модель для виходу (показ юзера) — це різні речі. Ніколи не лінуйтеся створювати окремі моделі.
- Безпека. Це ваш останній рубіж захисту від витоку даних.
- Документація. Response Model автоматично генерує красиву документацію (Swagger/OpenAPI), щоб фронтендери знали, що їм прийде.
3. 🧪 Приклади (від простого до реального)
Давайте писати код. Уявімо, що ми використовуємо FastAPI та Pydantic, оскільки це золотий стандарт сьогодення.
Приклад 1: "Товста" база даних
Спочатку подивимось на наш умовний об'єкт із бази даних (словник або об'єкт ORM):
# Наша "кухня" - дані з БД
fake_user_db = {
"id": 1,
"username": "david_malan",
"email": "malan@harvard.edu",
"password_hash": "super_secret_hash_123", # 😱 ЦЕ НЕ МОЖНА ПОКАЗУВАТИ!
"is_admin": True
}
Приклад 2: Рішення "В лоб" (Неправильне)
@app.get("/user")
def get_user():
return fake_user_db
Питання: Що отримає клієнт?
Відповідь: Він отримає все, включаючи password_hash. Це провал. ❌
Приклад 3: Використання Response Model (Правильне) ✅
Створимо модель-фільтр. Ми хочемо показати тільки username та email.
from pydantic import BaseModel
# Визначаємо, як має виглядати наша "чиста тарілка"
class UserResponse(BaseModel):
username: str
email: str
@app.get("/user", response_model=UserResponse) # <--- Магія тут!
def get_user():
return fake_user_db
Чому це круто?
Ми повернули fake_user_db (де є пароль!), але FastAPI подивився на response_model=UserResponse, побачив, що там немає поля password_hash, і автоматично його видалив перед відправкою.
Клієнт отримає лише:
{
"username": "david_malan",
"email": "malan@harvard.edu"
}
Чисто, безпечно, елегантно. 😎
4. 🛠 Практична частина
А тепер ваша черга! Відкрийте редактор (або уявіть його).
🔹 Завдання 1: Базовий фільтр
У вас є товар: product = {"id": 55, "name": "Phone", "cost_price": 500, "selling_price": 1000}.
Створіть Pydantic-модель ProductPublic, яка приховає cost_price (собівартість), щоб конкуренти не дізналися вашу маржу.
🔹 Завдання 2: Аліаси (Rename)
У базі даних поле називається selling_price, але фронтендери просять, щоб у JSON приходило просто price.
Підказка: Не міняйте базу! Змініть тільки модель. Дослідіть, як у Pydantic просто назвати змінну в моделі price, а значення взяти з selling_price (або просто присвоїти при поверненні).
🔹 Завдання 3: Обчислювані поля
У базі даних є first_name та last_name. Клієнт хоче одне поле full_name.
Напишіть модель, яка приймає ім'я та прізвище, а віддає їх об'єднаними. (Тут можна використати Pydantic field_validator або просто сформувати словник перед поверненням).
🔹 Завдання 4: Реальний кейс (User Profile)
Створіть дві моделі для одного й того самого юзера:
1. UserProfilePrivate (бачить сам власник): включає email, телефон, історію замовлень.
2. UserProfilePublic (бачать інші люди): тільки ім'я та аватарка.
Напишіть псевдо-код двох ендпоінтів (/me та /users/{id}), які використовують ці різні моделі для одного джерела даних.
🔹 Завдання 5: Питання "А що, якщо..."
Що станеться, якщо в response_model ви вказали поле age: int, а в базі даних (у словнику, який ви повертаєте) цього ключа взагалі немає?
* А) API поверне null?
* Б) API впаде з помилкою 500 Internal Server Error?
* В) Pydantic придумає число сам?
(Поміркуйте і перевірте).
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі в цій темі?
❌ Типова помилка новачка:
Використовувати одну модель User для всього: і для реєстрації (вхідні дані), і для логіну, і для профілю, і для адмінки.
Наслідок: Ви хочете додати поле confirm_password для реєстрації, і воно раптово починає "стирчати" у відповіді профілю. Це "спагетті-моделі".
✅ Як думає профі:
"Контракт — понад усе". Спочатку я домовляюся з фронтендом/мобільним додатком, що саме їм потрібно отримати. Я пишу Response Model. І тільки потім я думаю, як дістати ці дані з бази. Це називається API-First Design.
💡 Порада з практики:
Якщо ви змінюєте структуру бази даних (наприклад, перейменували колонку), ваша Response Model захищає API від поломки. Ви просто міняєте логіку всередині функції, а модель залишається старою. Клієнти навіть не помітять, що ви переробили всю базу. Це називається Desoupling (розчеплення).
6. 🧩 Підсумок
Отже, друзі, що ми маємо у сухому залишку?
- Ми ніколи не віддаємо об'єкти з бази напряму. Це негігієнічно і небезпечно.
- Response Model — це наш офіціант, який перевіряє тарілку перед подачею.
- Ми використовуємо різні моделі для різних ситуацій (публічні дані vs приватні).
Тепер ви вмієте: Контролювати дані, захищати секрети та будувати стабільні API, які не ламаються від кожного чиху в базі даних.
👉 Наступного разу: Тепер, коли ми знаємо, як віддавати дані, поговоримо про те, як їх приймати і перевіряти. Готуйтеся, наступна тема: Валідація вхідних даних: чому не можна вірити користувачам.
Це був CS50... тобто, наш урок! 😉 Кодьте з розумом! 👨💻👩💻