Fullstack на JavaScript — API и фронтенд
Fullstack на JavaScript — API и фронтенд
Что такое fullstack в этой статье
Fullstack здесь — два приложения на JavaScript, которые работают вместе:
- Бэкенд (API) — Node + Express, порт
3000, отдаёт JSON (262, 263). - Фронтенд (UI) — React, Vue или Next в браузере, рисует кнопки и списки (272, 282, 2731).
Вы уже умеете поднимать каждую часть отдельно. На практике их запускают одновременно в двух терминалах — и сталкиваются с CORS и портами. Эта статья — карта склейки без магии.
Схема — кто с кем говорит
| Часть | Порт (пример) | Роль |
|---|---|---|
| API | 3000 | Данные, правила, позже БД |
| Vite (React/Vue) | 5173 | SPA: один HTML, дальше JS |
| Next.js | 3000 по умолчанию | Конфликт с API — сдвиньте API на 3001 или Next на 3001 |
Браузер выполняет JS фронта. JS вызывает fetch('http://127.0.0.1:3000/notes') — это отдельный HTTP-запрос с страницы на другой порт.
Шаг 1 — API должен отвечать без фронта
В каталоге API:
cd notes-api
npm run dev
Проверка во втором терминале (или в том же, если сервер в фоне):
curl http://127.0.0.1:3000/health
curl http://127.0.0.1:3000/notes
Ожидаемо: JSON и код 200. Если Connection refused — сервер не слушает порт; фронт это не исправит. Вернитесь к 262.
Шаг 2 — origin и CORS
Origin = протокол + хост + порт, например http://localhost:5173.
Страница с origin 5173 запрашивает API на 3000 — для браузера это кросс-доменный запрос. Сервер должен явно разрешить его заголовками CORS.
В API:
npm install cors
import cors from 'cors';
app.use(
cors({
origin: ['http://127.0.0.1:5173', 'http://localhost:5173'],
methods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
}),
);
Подробнее про цепочку middleware: 263.md. В production в origin — домен вашего сайта, не *, если нужны cookie.
Важно: Postman и curl CORS не проверяют. Ошибка «в консоли браузера CORS» при рабочем curl — нормальная ситуация для новичка.
Шаг 3 — fetch на клиенте
fetch — встроенный в браузер способ сделать HTTP-запрос. Возвращает Promise; ответ нужно проверить и распарсить:
const API = 'http://127.0.0.1:3000';
async function loadNotes() {
const res = await fetch(`${API}/notes`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const notes = await res.json();
return notes;
}
| Часть выражения | Смысл |
|---|---|
`${API}/notes` | Шаблонная строка: склеить базовый URL и путь |
await fetch(...) | Дождаться ответа сервера |
res.ok | true, если статус 200–299 |
res.json() | Тело ответа как объект JavaScript |
| Фреймворк | Где вызывать |
|---|---|
| React | useEffect при монтировании — 272 |
| Vue | onMounted — 282 |
| Next (client) | 'use client' + useEffect — 2731 |
| Next (server) | async Server Component: запрос идёт с сервера Next, CORS в браузере не участвует; API должен быть доступен с машины, где крутится Next |
Шаг 4 — прокси в dev (альтернатива CORS)
Идея: браузер ходит на тот же origin, что и страница (5173), а dev-сервер тихо пересылает запрос на API.
Vite (vite.config.js):
export default {
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
};
| Настройка | Зачем |
|---|---|
target | Куда слать запрос на самом деле |
rewrite | /api/notes → /notes на бэкенде |
changeOrigin | Подменить заголовок Host для некоторых серверов |
Во фронте:
const res = await fetch('/api/notes'); // origin остаётся :5173
Next.js (next.config.ts):
const nextConfig = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'http://127.0.0.1:3000/:path*',
},
];
},
};
export default nextConfig;
Прокси удобен в разработке; в production часто отдельный домен API + CORS или один домен за Nginx.
Два терминала — рабочий ритуал
Терминал 1: cd notes-api && npm run dev → слушает :3000
Терминал 2: cd notes-ui && npm run dev → открыть :5173 в браузере
В Chrome: F12 → Network → обновить страницу → кликнуть запрос notes → вкладка Preview: массив JSON, статус 200.
Если статус (failed) или CORS — API, cors или прокси; если 404 — неверный путь или rewrite.
POST и DELETE с фронта
Создание заметки:
await fetch(`${API}/notes`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: 'новая заметка' }),
});
| Поле | Зачем |
|---|---|
method: 'POST' | HTTP-метод «создать» |
Content-Type | Сервер знает, что тело — JSON (express.json() на бэке) |
body: JSON.stringify(...) | Объект JS → строка для провода |
Удаление:
await fetch(`${API}/notes/1`, { method: 'DELETE' });
После POST обновите список в state: снова вызовите loadNotes() или добавьте заметку в массив вручную — иначе UI останется со старыми данными.
Частые ошибки
| Симптом | Причина | Решение |
|---|---|---|
Failed to fetch | API выключен или неверный URL | curl /health, проверить терминал 1 |
| CORS в консоли | Нет cors или другой origin | 263 или прокси Vite |
ECONNREFUSED | Порт/хост не тот | Везде 127.0.0.1 или везде localhost |
| Next и API оба на 3000 | Конфликт портов | API → PORT=3001 |
| Пустой массив, 200 | In-memory store сбросился при перезапуске API | Ожидаемо для 262 |
404 на /api/notes | Прокси без rewrite | Путь на бэке — /notes, не /api/notes |
Что попробовать
- Один API — два UI (React и Vue) с одного
originвcors. - Route Handler в Next, который проксирует на Node: секреты API остаются на сервере.
- Тот же сценарий «Заметки» на Python: Flask 3411.
Дальше
| Тема | Материал |
|---|---|
| Express углубление | 263.md |
| React / Vue / Next | 272 · 282 · 2731 |
| Тесты API | 33.md |
| Чек-лист | 999.md |
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Основы JavaScript - стандарт ECMAScript, модель выполнения и базовые конструкции языка. JavaScript — это язык программирования, который позволяет создавать интерактивные веб-страницы, серверные приложения и мобильные программы. Для создания массивов используется литеральная нотация. Конструктор Array не применяется. Как работать с HTML-элементами, как их создавать, менять. Простые приложения на JavaScript - базовые сценарии, структура кода и быстрый старт с практическими примерами. Расширения файлов определяют способ обработки кода средой выполнения или компилятором. История JavaScript - происхождение языка, ключевые этапы развития и влияние на современный веб. Такое именование представляет собой соглашение между разработчиками. Классический JavaScript не обеспечивает реальной приватности через подчеркивания. JavaScript содержит набор зарезервированных слов, которые имеют специальное значение в языке. Эти слова нельзя использовать в качестве идентификаторов для переменных, функций или классов. Встроенные функции JavaScript - ключевые методы массивов, строк и объектов для повседневной разработки. Этот шаблон описывает подключение внешних функций, классов или значений из других файлов. Он используется в начале файла и определяет зависимости текущего модуля. JavaScript используется для создания кроссплатформенных мобильных приложений, которые работают на iOS и Android с использованием единой кодовой базы.Основы JavaScript
Что требуется знать перед началом изучения языка программирования JavaScript
Рекомендации по разработке на JavaScript
Работа с HTML в JavaScript
Простые приложения на JavaScript
Форматы JavaScript
История языка JavaScript
Синтаксис и пунктуация в JavaScript
Ключевые слова языка JavaScript
Встроенные функции JavaScript
Структура и подключение JavaScript-кода
Применение JavaScript в вебе и за его пределами