Собрал форму записи для клиники через Claude Code: от пустого файла до продакшена
Задача простая на словах: форма записи для клиники — имя, телефон, услуга. Заполнил — заявка падает в Telegram администратору, форма живёт на своём домене и работает ночью. Я не программист, я бывший руководитель продаж. Собрал это через Claude Code, без классического кодинга. Показываю весь путь — вместе с местами, где я споткнулся.
Задачу надо ставить как ТЗ, а не как желание
Главная ошибка новичка — написать Claude Code «сделай форму записи» и ждать магии. Он что-нибудь сделает, но не то, что у тебя в голове. Claude Code силён ровно настолько, насколько точно ты назвал результат. Формулируй как инженер, который принимает работу: какие поля, куда уходит заявка, что должно произойти после отправки.
✕ «сделай форму записи для клиники»
✕ ИИ угадывает поля, дизайн, логику
✕ переделываешь три раза
✓ «форма: имя, телефон, услуга (выпадающий список)»
✓ «POST на /api/booking, тело — JSON»
✓ «после отправки — заявка в Telegram + запись в базу»
Разница не в вежливости, а в количестве переделок. На размытой формулировке ты потратишь вечер на споры с ИИ. На точной — он соберёт скелет с первого раза, а ты будешь только докручивать. Я держу в голове три вопроса перед каждой задачей: что на входе, куда уходит, что происходит после. Ответил на все три — можешь формулировать.
Фронт формы: три поля, валидация телефона и ловушка для ботов
Начинаем с того, что видит пациент. Обычная HTML-форма: имя, телефон, услуга. Две вещи, которые прошу у Claude Code сразу, — проверку телефона (чтобы не приходили заявки с пустым номером) и honeypot: скрытое поле, которое человек не видит и не заполнит, а бот заполнит. Заполнено — значит бот, заявку молча выкидываем.
<form id="booking">
<input name="name" placeholder="Имя" required>
<input name="phone" placeholder="+7 ___ ___ __ __"
pattern="\+?[0-9\s\-]{10,}" required>
<select name="service" required>
<option value="">Услуга</option>
<option>Консультация</option>
<option>УЗИ</option>
</select>
<!-- ловушка для ботов: человек её не видит -->
<input name="website" tabindex="-1" autocomplete="off"
style="position:absolute;left:-9999px">
<button>Записаться</button>
</form>
<script>
document.getElementById('booking').onsubmit = async (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
const res = await fetch('/api/booking', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
alert(res.ok ? 'Заявка принята' : 'Ошибка, позвоните нам');
};
</script>
Валидацию через pattern браузер делает сам, без единой строки логики. Это не защита от всего, но пустые и мусорные номера отсекает на входе. Дальше — fetch отправляет собранные поля на бэкенд-эндпоинт. Всё, что видит пациент, уместилось в один экран.
Бэкенд-эндпоинт: куда падает заявка
Фронт красивый, но сам по себе он никуда ничего не отправит. Нужен серверный роут — адрес, куда прилетает POST, и который делает две вещи: шлёт заявку в Telegram администратору и пишет её в базу, чтобы ничего не потерялось. Токены и ключи — только на сервере, в переменных окружения. На фронт их выносить нельзя: любой откроет код страницы и заберёт.
// /api/booking — серверный роут (Next.js-стиль)
export async function POST(req) {
const { name, phone, service, website } = await req.json();
// honeypot заполнен → это бот, тихо выходим
if (website) return Response.json({ ok: true });
// валидация на сервере, не только в браузере
if (!name || !/[0-9]{10,}/.test((phone||'').replace(/\D/g,'')))
return Response.json({ ok: false }, { status: 400 });
// 1. сигнал в Telegram администратору
await fetch(`https://api.telegram.org/bot${process.env.TG_TOKEN}/sendMessage`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
chat_id: process.env.TG_CHAT,
text: `Новая запись\nИмя: ${name}\nТелефон: ${phone}\nУслуга: ${service}`
})
});
// 2. пишем в базу сервисной ролью (см. грабли ниже)
await supabaseAdmin.from('bookings').insert({ name, phone, service });
return Response.json({ ok: true });
}
Три защиты, которые прошу заложить сразу: honeypot проверяем и на сервере (не только в браузере — бот шлёт запрос напрямую, минуя форму); валидацию дублируем на сервере; и хорошо бы поставить простой лимит — не больше N заявок с одного адреса в минуту, чтобы форму не завалили тысячей отправок. Последнее можно добавить позже, но заложить в голову — сейчас.
Git: фиксируем работу, чтобы не потерять
Пока всё лежит только на твоём ноутбуке — этого не существует. Git — это точки сохранения. Работаю в отдельной ветке, чтобы не трогать рабочую версию сайта, фиксирую изменения коммитом и отправляю на сервер. Три команды, которые прошу Claude Code выполнить и которые ты быстро запомнишь сам:
git checkout -b booking-form # новая ветка под форму
git add . # собрать изменения
git commit -m "форма записи + эндпоинт /api/booking"
git push origin booking-form # отправить на GitHub
Отдельная ветка — это страховка. Если что-то сломается, рабочий сайт стоит нетронутым, а ты спокойно доводишь форму в своей ветке. Claude Code сам подскажет команды, но понимать, что делает каждая, стоит — это твои точки отката.
Vercel: push в Git → форма в проде
Дальше самое приятное. Vercel привязан к репозиторию: как только код уходит в Git, он сам собирает и публикует сайт. Никаких серверов руками. После деплоя первым делом проверяю, что эндпоинт живой — бью в него из терминала curl, не открывая форму:
# проверяем, что эндпоинт принимает заявку
curl -X POST https://klinika.kz/api/booking \
-H "Content-Type: application/json" \
-d '{"name":"Тест","phone":"+77001234567","service":"УЗИ"}'
# ждём в ответ: {"ok":true} и сообщение в Telegram
Пришёл {"ok":true} и сообщение в Telegram — бэкенд работает. Осталось подключить домен. В Vercel добавляешь свой адрес, он выдаёт, что прописать у регистратора: либо A-запись на IP Vercel, либо сменить nameservers на серверы Vercel. Второе проще — Vercel сам рулит всеми записями. DNS обновляется от нескольких минут до пары часов, тут остаётся только подождать.
Грабли: где я реально споткнулся
Теперь честная часть, ради которой стоит читать чужой опыт. Каждый из этих трёх пунктов стоил мне времени, и на каждом я сначала грешил на ИИ, а виноват был не он.
База молча не писала заявки. Форма отправлялась, Telegram приходил, а в базе пусто. Причина — защита доступа на уровне строк (RLS): по умолчанию она запрещает запись анонимному клиенту. Это правильно, что она включена, но эндпоинт должен писать сервисной ролью — отдельным серверным ключом, который эти ограничения обходит легально. Как только эндпоинт пошёл в базу сервисным ключом (а не публичным), заявки легли на место. Публичный ключ на фронт, сервисный — только на сервер.
CORS резал запрос. Пока форма и эндпоинт на одном домене — всё гладко. Стоит вынести форму на отдельный адрес — браузер начинает блокировать запрос с чужого источника. Лечится заголовками на эндпоинте, разрешающими твой домен. Знать про это заранее — сэкономить полдня гадания, почему «в одном месте работает, в другом нет».
Сессии и переменные окружения. Локально всё работало, в проде эндпоинт падал. Оказалось — забыл добавить TG_TOKEN и ключи базы в переменные окружения на самом Vercel. На ноутбуке они лежали в локальном файле, а в облако не уехали (и правильно — секреты в Git не коммитят). Правило: каждый ключ, который есть локально, продублируй в настройках Vercel.
Задачу ставь как ТЗ. Секреты — только на сервере. В базу пиши сервисной ролью. Проверяй эндпоинт через curl до того, как открыл форму. И тогда «собрать форму записи» — это вечер, а не проект на месяц.
Это ровно тот же принцип, на котором держатся мои большие системы: маленькие проверяемые кирпичики, каждый из которых можно потрогать отдельно. Если хочешь увидеть, как из таких кирпичиков собирается связка ИИ с живой базой бизнеса — я разбирал это в отдельной статье про подключение Claude к CRM за вечер.
Два пути дальше
У тебя клиника или бизнес и нужна такая же форма записи, которая шлёт заявку сразу в Telegram и не теряет ни одной, — напиши слово РАЗБОР в директ @la_konstant: разберём твою воронку записи за 60 минут. Хочешь сам научиться собирать такие вещи через Claude Code, без классического программирования, — напиши ОБУЧЕНИЕ в директ, беру 2–3 человека на реальных проектах.
Написать в директ → @la_konstant