Найти в Дзене

От бэкенда до фронтенда: полное руководство по созданию менеджера задач на FastAPI и React

В мире современной веб-разработки связка высокопроизводительного бэкенда на Python и динамичного фронтенда на JavaScript стала золотым стандартом. FastAPI, с его невероятной скоростью, автоматической документацией и типизацией Python, идеально подходит для создания надежных API. С другой стороны, React остается неоспоримым королем в построении интерактивных, компонентных пользовательских интерфейсов. Но как заставить их «общаться» друг с другом? Как обрабатывать стилизацию, управлять состоянием и избегать пресловутых ошибок CORS? В этом исчерпывающем руководстве я проведу вас через весь процесс создания full-stack приложения с нуля: стильного, современного менеджера задач. Мы не просто напишем код; мы поймем, почему мы его пишем. Наш план действий: Готовы? Давайте погружаться. Сначала построим двигатель нашего приложения. 1. Структура проекта и виртуальное окружение Чистая структура — ключ к успеху. Создадим основную папку проекта и подпапку backend. mkdir fastapi-react-tasker
cd fasta
Оглавление

В мире современной веб-разработки связка высокопроизводительного бэкенда на Python и динамичного фронтенда на JavaScript стала золотым стандартом. FastAPI, с его невероятной скоростью, автоматической документацией и типизацией Python, идеально подходит для создания надежных API. С другой стороны, React остается неоспоримым королем в построении интерактивных, компонентных пользовательских интерфейсов.

Но как заставить их «общаться» друг с другом? Как обрабатывать стилизацию, управлять состоянием и избегать пресловутых ошибок CORS?

В этом исчерпывающем руководстве я проведу вас через весь процесс создания full-stack приложения с нуля: стильного, современного менеджера задач. Мы не просто напишем код; мы поймем, почему мы его пишем.

Наш план действий:

  1. Сначала бэкенд: Настроим мощный бэкенд на FastAPI для управления нашими задачами.
  2. Основа фронтенда: Создадим приложение на React с помощью Vite для молниеносной разработки.
  3. Стилизация с Tailwind CSS: Построим красивый, адаптивный интерфейс, не написав ни единой строчки кастомного CSS.
  4. Соединяем точки: Используем Axios, чтобы наш фронтенд и бэкенд бесшовно общались.
  5. Полировка приложения: Добавим состояния загрузки и обработку ошибок для профессионального пользовательского опыта.

Готовы? Давайте погружаться.

Часть 1: Создание API на FastAPI

Сначала построим двигатель нашего приложения.

1. Структура проекта и виртуальное окружение

Чистая структура — ключ к успеху. Создадим основную папку проекта и подпапку backend.

mkdir fastapi-react-tasker
cd fastapi-react-tasker
mkdir backend
cd backend

Внутри backend создадим виртуальное окружение Python. Это обязательная лучшая практика для изоляции зависимостей проекта.

# Для macOS/Linux
python3 -m venv venv
source venv/bin/activate

# Для Windows
python -m venv venv
venv\Scripts\activate

Вы увидите (venv) в командной строке, что означает, что окружение активно.

2. Установка зависимостей

Нам нужны две основные библиотеки: fastapi для самого фреймворка и uvicorn для запуска сервера.

pip install fastapi "uvicorn[standard]"

3. Создание API (main.py)

Создайте файл main.py внутри папки backend. Здесь мы определим наши модели данных, "базу данных" в памяти и эндпоинты API.

# backend/main.py

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List

# --- Модели Pydantic для валидации данных ---
class TaskBase(BaseModel):
title: str
is_completed: bool = False

class TaskCreate(TaskBase):
pass

class Task(TaskBase):
id: int

# --- "База данных" в памяти ---
tasks_db = {
1: Task(id=1, title="Изучить FastAPI", is_completed=True),
2: Task(id=2, title="Создать React-приложение", is_completed=False),
3: Task(id=3, title="Стилизовать с Tailwind CSS", is_completed=False),
}
next_task_id = 4

# --- Инициализация приложения FastAPI ---
app = FastAPI(
title="Task Manager API",
description="Простое API для управления задачами.",
version="1.0.0",
)

# --- Middleware для CORS (Cross-Origin Resource Sharing) ---
# Это критически важно! Позволяет нашему React-приложению (на другом порту)
# отправлять запросы к нашему FastAPI-бэкенду.
origins = [
"http://localhost:5173",
"http://127.0.0.1:5173",
]

app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# --- Эндпоинты API ---

@app.get("/api/tasks", response_model=List[Task], tags=["Tasks"])
async def get_all_tasks():
"""Получить все задачи."""
return list(tasks_db.values())

@app.post("/api/tasks", response_model=Task, status_code=201, tags=["Tasks"])
async def create_new_task(task: TaskCreate):
"""Создать новую задачу."""
global next_task_id
new_task = Task(id=next_task_id, **task.dict())
tasks_db[next_task_id] = new_task
next_task_id += 1
return new_task

@app.delete("/api/tasks/{task_id}", status_code=204, tags=["Tasks"])
async def delete_existing_task(task_id: int):
"""Удалить задачу по ID."""
if task_id not in tasks_db:
raise HTTPException(status_code=404, detail="Задача не найдена")
del tasks_db[task_id]
return

@app.patch("/api/tasks/{task_id}", response_model=Task, tags=["Tasks"])
async def update_task_status(task_id: int, updates: dict):
"""Обновить статус выполнения задачи."""
if task_id not in tasks_db:
raise HTTPException(status_code=404, detail="Задача не найдена")

task = tasks_db[task_id]
if 'is_completed' in updates:
task.is_completed = updates['is_completed']

return task

4. Запуск бэкенда

В терминале (в директории backend) запустите сервер:

uvicorn main:app --reload

Откройте в браузере http://127.0.0.1:8000/docs. Вы увидите красивую интерактивную документацию Swagger UI, которую FastAPI сгенерировал для вас.

Часть 2: Создание фронтенда на React с Tailwind CSS

Теперь, когда наш API работает, займемся пользовательским интерфейсом.

1. Создание React-приложения с помощью Vite

Вернитесь в корневую папку проекта и используйте Vite для создания нового React-проекта в папке frontend.

cd .. # Возвращаемся в корень проекта
npm create vite@latest frontend -- --template react-ts

(Я использую шаблон react-ts для TypeScript, что настоятельно рекомендуется для современных React-проектов. )

Следуйте инструкциям, затем перейдите в новую директорию и установите зависимости.

cd frontend
npm install

2. Установка и настройка Tailwind CSS

Далее — стили. Мы будем следовать официальному руководству.

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Это создаст файлы tailwind.config.js и postcss.config.js. Настроим Tailwind для сканирования ваших файлов. Откройте tailwind.config.js и измените массив content:

// frontend/tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}", // Указываем Tailwind, где искать классы
],
theme: {
extend: {},
},
plugins: [],
}

Наконец, добавьте директивы Tailwind в ваш основной CSS-файл. Замените содержимое src/index.css этим:

/* frontend/src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

3. Установка Axios

Нам нужна библиотека для выполнения HTTP-запросов. axios — это промышленный стандарт.

npm install axios

4. Создание UI (App.tsx)

Теперь самое интересное! Заменим содержимое src/App.tsx нашей логикой и JSX, стилизованным с помощью Tailwind.

// frontend/src/App.tsx

import React, { useState, useEffect, FormEvent } from 'react';
import axios from 'axios';

// --- Интерфейсы TypeScript ---
interface Task {
id: number;
title: string;
is_completed: boolean;
}

// --- Конфигурация API ---
const API_URL = 'http://127.0.0.1:8000/api';

function App( ) {
const [tasks, setTasks] = useState<Task[]>([]);
const [newTaskTitle, setNewTaskTitle] = useState("");
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

// --- Загрузка задач с API ---
useEffect(() => {
const fetchTasks = async () => {
try {
const response = await axios.get(`${API_URL}/tasks`);
setTasks(response.data);
} catch (err) {
setError("Не удалось загрузить задачи. Бэкенд запущен?");
} finally {
setLoading(false);
}
};
fetchTasks();
}, []);

// --- Создание новой задачи ---
const handleCreateTask = async (e: FormEvent) => {
e.preventDefault();
if (!newTaskTitle.trim()) return;

try {
const response = await axios.post(`${API_URL}/tasks`, { title: newTaskTitle });
setTasks([...tasks, response.data]);
setNewTaskTitle("");
} catch (err) {
setError("Не удалось создать задачу.");
}
};

// --- Удаление задачи ---
const handleDeleteTask = async (taskId: number) => {
try {
await axios.delete(`${API_URL}/tasks/${taskId}`);
setTasks(tasks.filter(task => task.id !== taskId));
} catch (err) {
setError("Не удалось удалить задачу.");
}
};

// --- Переключение статуса задачи ---
const handleToggleTask = async (taskId: number, currentStatus: boolean) => {
try {
const response = await axios.patch(`${API_URL}/tasks/${taskId}`, { is_completed: !currentStatus });
setTasks(tasks.map(task => task.id === taskId ? response.data : task));
} catch (err) {
setError("Не удалось обновить задачу.");
}
};

return (
<div className="bg-slate-900 min-h-screen flex items-center justify-center font-sans">
<div className="w-full max-w-lg bg-slate-800 shadow-lg rounded-xl p-8">
<h1 className="text-4xl font-bold text-white text-center mb-8">Менеджер задач</h1>

{error && <p className="text-red-500 bg-red-900/50 p-3 rounded-md text-center mb-4">{error}</p>}

<form onSubmit={handleCreateTask} className="flex gap-4 mb-8">
<input
type="text"
value={newTaskTitle}
onChange={(e) => setNewTaskTitle(e.target.value)}
placeholder="Добавить новую задачу..."
className="flex-grow bg-slate-700 text-white placeholder-slate-400 rounded-md p-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition"
/>
<button
type="submit"
className="bg-indigo-600 text-white font-semibold px-6 py-3 rounded-md hover:bg-indigo-500 transition disabled:bg-slate-600"
disabled={!newTaskTitle.trim()}
>
Добавить
</button>
</form>

<div className="space-y-4">
{loading ? (
<p className="text-slate-400 text-center">Загрузка задач...</p>
) : (
tasks.map(task => (
<div
key={task.id}
className="bg-slate-700 p-4 rounded-lg flex items-center justify-between transition hover:bg-slate-600"
>
<div className="flex items-center gap-4">
<input
type="checkbox"
checked={task.is_completed}
onChange={() => handleToggleTask(task.id, task.is_completed)}
className="h-6 w-6 rounded bg-slate-600 border-slate-500 text-indigo-600 focus:ring-indigo-500"
/>
<span className={`text-lg ${task.is_completed ? 'line-through text-slate-500' : 'text-white'}`}>
{task.title}
</span>
</div>
<button
onClick={() => handleDeleteTask(task.id)}
className="text-slate-500 hover:text-red-500 transition"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
))
)}
</div>
</div>
</div>
);
}

export default App;

5. Запуск фронтенда

В терминале (в папке frontend) запустите сервер разработки:

npm run dev

Ваше React-приложение теперь будет работать по адресу http://localhost:5173.

Часть 3: Гранд-финал

Убедитесь, что у вас запущены оба процесса в двух разных терминалах:

  1. Бэкенд FastAPI на http://127.0.0.1:8000
  2. Фронтенд React на http://localhost:5173

Теперь откройте в браузере http://localhost:5173.

Вы должны увидеть ваш красивый, полностью функциональный менеджер задач! Попробуйте добавить задачу, отметить ее как выполненную или удалить. Каждое действие бесшовно общается с вашим бэкендом на FastAPI, а интерфейс обновляется в реальном времени.

Заключение и следующие шаги

Поздравляю! Вы только что создали полноценное веб-приложение с использованием FastAPI, React и Tailwind CSS. Вы решили некоторые из самых распространенных проблем в современной веб-разработке, включая проектирование API, управление состоянием на фронтенде и междоменные запросы.

Этот проект — фантастическая основа. Отсюда вы можете:

  • Добавить настоящую базу данных: Заменить список в памяти на PostgreSQL с помощью ORM, такой как SQLAlchemy.
  • Реализовать аутентификацию пользователей: Использовать JWT-токены для создания списков задач для конкретных пользователей.
  • Развернуть ваше приложение: Задеплоить бэкенд на Heroku или DigitalOcean, а фронтенд — на Vercel или Netlify.

Теперь у вас есть ключевые навыки для создания сложных, современных веб-приложений. Возможности безграничны.

Удачного кодинга!