App React que consume tu API de Pasteles

 

App React que consume tu API de Pasteles 馃嵃

Vamos a crear una aplicaci贸n en React con Vite (la forma moderna y recomendada) que consuma tu endpoint http://localhost:8000/api/pasteles.


馃搵 Requisitos previos

Antes de empezar, aseg煤rate de tener instalado:

HerramientaVerificar conVersi贸n m铆nima
Node.jsnode -vv18+
npmnpm -vv9+
Laravel corriendophp artisan serve—

Si no tienes Node.js, desc谩rgalo de nodejs.org.

Importante: Deja Laravel corriendo en otra terminal:

bash
php artisan serve

Debe estar disponible en http://localhost:8000.


1️⃣ Crear el proyecto React con Vite

En una terminal nueva (distinta a la de Laravel), ve a la carpeta donde quieras tu proyecto y ejecuta:

bash
npm create vite@latest pasteles-app -- --template react

Flujo completo

text
npm create vite@latest pasteles-app -- --template react
│         │                │          │  │
│         │                │          │  └─ opci贸n para create-vite
│         │                │          └──── separador: "lo de abajo es para el comando, no para npm"
│         │                └─────────────── nombre de la carpeta del proyecto
│         └──────────────────────────────── paquete generador (create-vite)
└────────────────────────────────────────── comando npm

Equivalencia sin el --

En muchos casos podr铆as escribir directamente:

bash
npx create-vite@latest pasteles-app --template react

Aqu铆 no necesitas el -- porque ya est谩s ejecutando create-vite directamente, no a trav茅s de npm. El -- es necesario solo cuando pasas flags a trav茅s de npm create.

 --template react

  • Es una bandera (flag) que pertenece a create-vite, no a npm.

  • Le dice a Vite: "genera el proyecto usando la plantilla de React" (en lugar de vanilla, vue, svelte, etc.).

  • Internamente Vite tiene plantillas predefinidas: vanilla, vue, react, react-ts, svelte, solid, etc.

Esto te preguntar谩 algunas cosas. Si te pregunta por instalar create-vite, responde y.

Luego entra a la carpeta e instala dependencias:

bash
cd pasteles-app
npm install

¿Por qu茅 Vite y no Create React App?

Create React App (npx create-react-app) est谩 oficialmente deprecado desde 2023. Vite es m谩s r谩pido, moderno y es lo que recomienda la comunidad actualmente.


2️⃣ Instalar Axios (para consumir la API)

Axios es la librer铆a est谩ndar para hacer peticiones HTTP. Desde la ra铆z del proyecto:

bash
npm install axios

Alternativa: puedes usar fetch() nativo sin instalar nada, pero Axios es m谩s c贸modo.


3️⃣ Estructura del proyecto

Tu proyecto debe verse as铆:

text
pasteles-app/
├── src/
│   ├── App.jsx          ← Componente principal (lo editaremos)
│   ├── App.css          ← Estilos (lo editaremos)
│   ├── main.jsx         ← Punto de entrada (no lo tocamos)
│   ├── index.css        ← Estilos globales (lo editaremos)
│   └── services/
│       └── pastelService.js   ← NUEVO: centraliza las llamadas a la API
├── index.html
└── package.json

4️⃣ Crear el servicio de API

Crea la carpeta src/services/ y dentro el archivo pastelService.js:

javascript
// src/services/pastelService.js
import axios from 'axios';

// URL base de tu API Laravel
const API_URL = 'http://localhost:8000/api';

const api = axios.create({
  baseURL: API_URL,
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
});

// GET /api/pasteles - Obtener todos los pasteles
export const getPasteles = async () => {
  const response = await api.get('/pasteles');
  return response.data; // { success: true, data: [...], message: "..." }
};

// GET /api/pasteles/:id - Obtener uno
export const getPastel = async (id) => {
  const response = await api.get(`/pasteles/${id}`);
  return response.data;
};

// POST /api/pasteles - Crear
export const createPastel = async (pastel) => {
  const response = await api.post('/pasteles', pastel);
  return response.data;
};

// PUT /api/pasteles/:id - Actualizar
export const updatePastel = async (id, pastel) => {
  const response = await api.put(`/pasteles/${id}`, pastel);
  return response.data;
};

// DELETE /api/pasteles/:id - Eliminar
export const deletePastel = async (id) => {
  const response = await api.delete(`/pasteles/${id}`);
  return response.data;
};

export default api;

馃挕 Centralizar las llamadas en un servicio es una buena pr谩ctica. Si ma帽ana cambia la URL o el endpoint, solo editas este archivo.


5️⃣ Crear el componente ListaPasteles

Crea src/components/ListaPasteles.jsx:

jsx
// src/components/ListaPasteles.jsx
import { useEffect, useState } from 'react';
import { getPasteles } from '../services/pastelService';
import PastelCard from './PastelCard';

function ListaPasteles() {
  const [pasteles, setPasteles] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    cargarPasteles();
  }, []);

  const cargarPasteles = async () => {
    try {
      setLoading(true);
      const respuesta = await getPasteles();

      if (respuesta.success) {
        setPasteles(respuesta.data);
      } else {
        setError('La API respondi贸 pero sin datos.');
      }
    } catch (err) {
      console.error(err);
      setError('No se pudo conectar con la API. ¿Est谩 corriendo Laravel?');
    } finally {
      setLoading(false);
    }
  };

  if (loading) return <p className="estado">⏳ Cargando pasteles...</p>;
  if (error)   return <p className="estado error">❌ {error}</p>;

  return (
    <div>
      <h1>馃嵃 Cat谩logo de Pasteles</h1>
      <p className="contador">Total: {pasteles.length} pasteles</p>

      <div className="grid-pasteles">
        {pasteles.map((pastel) => (
          <PastelCard key={pastel.id} pastel={pastel} />
        ))}
      </div>
    </div>
  );
}

export default ListaPasteles;

6️⃣ Crear el componente PastelCard

Crea src/components/PastelCard.jsx:

jsx
// src/components/PastelCard.jsx
function PastelCard({ pastel }) {
  const disponible = pastel.disponible === 1;

  return (
    <div className="pastel-card">
      <h3>{pastel.nombre}</h3>

      <span className="sabor">Sabor: {pastel.sabor}</span>

      <p className="precio">
        ${parseFloat(pastel.precio).toFixed(2)} MXN
      </p>

      <p className="descripcion">{pastel.descripcion}</p>

      <span className={disponible ? 'badge disponible' : 'badge agotado'}>
        {disponible ? '✅ Disponible' : '❌ Agotado'}
      </span>
    </div>
  );
}

export default PastelCard;

馃搶 Ojo: en tu JSON, disponible viene como 1 o 0 (porque es un boolean de MySQL). Por eso comparo con === 1.


7️⃣ Actualizar App.jsx

Reemplaza el contenido de src/App.jsx:

jsx
// src/App.jsx
import ListaPasteles from './components/ListaPasteles';
import './App.css';

function App() {
  return (
    <div className="app">
      <ListaPasteles />
    </div>
  );
}

export default App;

8️⃣ Estilos b谩sicos

Reemplaza src/App.css:

css
.app {
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
  font-family: system-ui, -apple-system, sans-serif;
}

h1 {
  text-align: center;
  color: #8b4513;
}

.contador {
  text-align: center;
  color: #666;
  margin-bottom: 2rem;
}

.grid-pasteles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.5rem;
}

.pastel-card {
  border: 1px solid #eee;
  border-radius: 12px;
  padding: 1.25rem;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  transition: transform 0.2s, box-shadow 0.2s;
}

.pastel-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}

.pastel-card h3 {
  margin: 0 0 0.5rem;
  color: #333;
  font-size: 1.1rem;
}

.sabor {
  display: inline-block;
  background: #fce4ec;
  color: #ad1457;
  padding: 0.2rem 0.6rem;
  border-radius: 6px;
  font-size: 0.8rem;
  text-transform: capitalize;
  margin-bottom: 0.5rem;
}

.precio {
  font-size: 1.4rem;
  font-weight: bold;
  color: #2e7d32;
  margin: 0.5rem 0;
}

.descripcion {
  color: #666;
  font-size: 0.9rem;
  line-height: 1.4;
  margin-bottom: 1rem;
}

.badge {
  display: inline-block;
  padding: 0.25rem 0.75rem;
  border-radius: 20px;
  font-size: 0.8rem;
  font-weight: 600;
}

.badge.disponible {
  background: #e8f5e9;
  color: #2e7d32;
}

.badge.agotado {
  background: #ffebee;
  color: #c62828;
}

.estado {
  text-align: center;
  font-size: 1.2rem;
  margin-top: 4rem;
  color: #666;
}

.estado.error {
  color: #c62828;
}

Y en src/index.css (opcional, para el fondo):

css
body {
  margin: 0;
  background: #f9f7f4;
}

9️⃣ ⚠️ Configurar CORS en Laravel (CR脥TICO)

Si no haces esto, React no podr谩 conectarse a la API y ver谩s un error de CORS en la consola del navegador:

text
Access to XMLHttpRequest at 'http://localhost:8000/api/pasteles' 
from origin 'http://localhost:5173' has been blocked by CORS policy

Soluci贸n: configurar CORS en Laravel

En Laravel 11/12 el archivo config/cors.php puede no existir. Publ铆calo:

bash
php artisan config:publish cors

Ahora edita config/cors.php y aseg煤rate de que tenga:

php
<?php

return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['http://localhost:5173'], // puerto por defecto de Vite

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => false,
];

Y limpia la cach茅 de configuraci贸n:

bash
php artisan config:clear
php artisan cache:clear

Reinicia el servidor de Laravel:

bash
php artisan serve

⚠️ Si quieres permitir cualquier origen (solo en desarrollo, nunca en producci贸n), usa:

php
'allowed_origins' => ['*'],

馃敓 Ejecutar la aplicaci贸n React

En la terminal del proyecto React:

bash
npm run dev

Ver谩s algo como:

text
  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose

Abre tu navegador en http://localhost:5173 y deber铆as ver tu cat谩logo de pasteles. 馃帀


馃И Probar todo el flujo

Con dos terminales abiertas:

Terminal 1 — Laravel:

bash
cd tu-proyecto-laravel
php artisan serve
# → http://localhost:8000

Terminal 2 — React:

bash
cd pasteles-app
npm run dev
# → http://localhost:5173

Y listo. Si todo est谩 bien, ver谩s los 30 pasteles en formato de tarjetas.


馃悰 Soluci贸n de problemas comunes

ErrorCausaSoluci贸n
CORS policy blockedFalta configurar CORS en LaravelPaso 9 de este tutorial
Network Error / ERR_CONNECTION_REFUSEDLaravel no est谩 corriendoEjecuta php artisan serve
404 Not FoundURL incorrectaVerifica http://localhost:8000/api/pasteles en el navegador
Los pasteles no aparecen pero no hay errorEl JSON tiene forma distintaAbre la consola y revisa respuesta.data
Puerto 5173 ocupadoOtra app usa ese puertoVite usar谩 5174 autom谩ticamente, o ci茅rrala
disponible se ve raroViene como 1/0 no true/falseYa lo manejamos con === 1 en PastelCard

馃搧 Estructura final del proyecto React

text
pasteles-app/
├── src/
│   ├── components/
│   │   ├── ListaPasteles.jsx
│   │   └── PastelCard.jsx
│   ├── services/
│   │   └── pastelService.js
│   ├── App.jsx
│   ├── App.css
│   ├── index.css
│   └── main.jsx
├── index.html
├── package.json
└── vite.config.js

馃殌 Pr贸ximos pasos (opcional)

Una vez que funcione la lectura, puedes ampliar la app con:

  1. Formulario para crear pasteles — usando createPastel() que ya definimos.

  2. Bot贸n de eliminar — con deletePastel(id) y un confirm().

  3. Editar — modal con un formulario.

  4. Filtros — por sabor o disponibilidad.

  5. React Router — para tener vistas separadas (lista, detalle, formulario).

  6. Autenticaci贸n con Sanctum — que ya se instal贸 cuando corriste php artisan install:api.


✅ Resumen de comandos

bash
# 1. Crear app React
npm create vite@latest pasteles-app -- --template react
cd pasteles-app

# 2. Instalar dependencias
npm install
npm install axios

# 3. En Laravel: publicar y configurar CORS
php artisan config:publish cors
# editar config/cors.php
php artisan config:clear

# 4. Ejecutar
npm run dev

Con esto ya tienes un frontend React consumiendo tu API Laravel de pasteles

Comentarios

Entradas m谩s populares de este blog

Tutorial CRUD con Laravel 12-Pastel::factory(50)->create();

CRUD Completo con Laravel 12 - Gu铆a Pr谩ctica Paso a Paso-productos

CRUD en Laravel 12 con Git - Gu铆a para Principiantes