Cómo Crear un Proyecto Fullstack con Vite, Express, MySQL y Docker

Cómo Crear un Proyecto Fullstack con Vite, Express, MySQL y Docker



En este tutorial vas a aprender, paso a paso, a crear una aplicación completa (Fullstack). 
Usaremos: 
  • Vite → para construir la parte visual (frontend), que es lo que ve el usuario.
  • Express → para manejar la lógica y las peticiones (backend), que es el “cerebro” de la aplicación.
  • MySQL → para guardar los datos.
  • Docker → para que todo funcione dentro de contenedores y puedas levantarlo con un solo comando.

📌 1. Crear la carpeta del proyecto

Primero, necesitamos un lugar en tu computadora donde guardaremos todos los archivos de nuestra aplicación.

Abre una terminal (o consola) y escribe:
mkdir mi-proyecto-fullstack
cd mi-proyecto-fullstack
  • mkdir mi-proyecto-fullstack → Crea una carpeta llamada mi-proyecto-fullstack
  • cd mi-proyecto-fullstack → Entra dentro de esa carpeta para comenzar a trabajar allí.

📌2. Crear el Frontend con Vite

Ahora vamos a crear la parte visual de la aplicación (el frontend) usando Vite, que es una herramienta muy rápida para iniciar proyectos en React.

En la terminal (asegúrate de estar dentro de mi-proyecto-fullstack) escribe:

npm create vite@latest frontend
  • Esto creará una carpeta llamada frontend con la estructura básica de una aplicación en React.
  • Cuando te pregunte el framework, selecciona React (o React + JavaScript si no quieres usar TypeScript).

Luego, entra a esa carpeta y descarga las dependencias necesarias:

cd frontend
npm install

Finalmente, regresa a la carpeta principal del proyecto:

cd ..

💡 En este punto ya tienes lista la base del frontend, aunque aún no lo vamos a ejecutar.


📌3. Crear el Backend con Express

Ahora vamos a crear la parte del servidor (backend) usando Express. Esta parte será la encargada de recibir peticiones desde el frontend, consultar la base de datos y devolver la información.

En la terminal, escribe los siguientes comandos para crear la carpeta y configurarla:

mkdir backend
cd backend
npm init -y
npm install express mysql2 cors
npm install --save-dev nodemon
  • express: Framework para crear el servidor web.
  • mysql2: Conector para trabajar con bases de datos MySQL.
  • cors: Permite que el frontend pueda comunicarse con el backend.
  • nodemon: Herramienta para reiniciar el servidor automáticamente al hacer cambios (solo en desarrollo).

Una vez instaladas las dependencias, tu archivo backend/package.json debe quedar así:

{
  "name": "backend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon --legacy-watch index.js",
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^5.1.0",
    "mysql2": "^3.14.3"
  },
  "devDependencies": {
    "nodemon": "^3.1.10"
  }
}

💡 Este archivo indica las dependencias de tu backend y define los comandos para iniciarlo en modo desarrollo (npm run dev) o producción (npm start).


📌4. Crear el Servidor con Express

En este paso vamos a programar el servidor backend usando Express. Este servidor se conectará a la base de datos MySQL y enviará datos al frontend.

Dentro de la carpeta backend crea un archivo llamado index.js con el siguiente contenido:

const express = require('express');
const cors = require('cors');
const mysql = require('mysql2');

const app = express();
app.use(cors());
app.use(express.json());

const db = mysql.createConnection({
  host: 'db',
  user: 'root',
  password: 'root',
  database: 'miapp'
});

app.get('/', (req, res) => {
  res.send('Servidor funcionando 🚀');
});

app.get('/usuarios', (req, res) => {
  db.query('SELECT * FROM usuarios', (err, results) => {
    if (err) return res.status(500).json(err);
    res.json(results);
  });
});

app.listen(3000, () => {
  console.log('Backend escuchando en el puerto 3000');
});
  • app.use(cors()): Permite que el frontend se comunique con este servidor sin problemas de seguridad (CORS).
  • app.use(express.json()): Permite recibir datos en formato JSON desde el frontend.
  • mysql.createConnection(...): Configura la conexión con la base de datos MySQL (en este caso, dentro de Docker).
  • /: Ruta inicial para verificar si el servidor está funcionando.
  • /usuarios: Ruta que devuelve todos los usuarios de la tabla usuarios en la base de datos.

💡 Cuando arranques el backend, podrás acceder a http://localhost:3000 para ver el mensaje de prueba, y a http://localhost:3000/usuarios para obtener los datos.


📌5. Configurar MySQL

En este paso vamos a preparar la base de datos que usará nuestro servidor. La crearemos dentro de un contenedor de MySQL usando Docker.

Crearemos una base de datos llamada miapp y un usuario root con contraseña root (solo para desarrollo).

Archivo docker-compose.yml (estructura inicial)

version: '3.8'
services:
  db:
    image: mysql:8.0
    container_name: mysql_db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: miapp
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:
  • image: Usamos la versión 8.0 de MySQL.
  • MYSQL_ROOT_PASSWORD: Contraseña del usuario root.
  • MYSQL_DATABASE: Base de datos que se creará automáticamente al iniciar el contenedor.
  • ports: Expone el puerto 3306 para poder conectarnos desde el host.
  • volumes: Guarda los datos para que no se pierdan si el contenedor se reinicia.

💡 Más adelante en el tutorial agregaremos aquí también los servicios del backend y frontend para que todo se ejecute junto.


📌6. Crear el Dockerfile del Frontend

Ahora vamos a preparar el Dockerfile para el frontend. Este archivo define cómo se va a construir la imagen de nuestro proyecto React con Vite.

Archivo frontend/Dockerfile

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 5173
CMD ["npm", "run", "dev", "--", "--host"]

Explicación línea por línea

  • FROM node:22-alpine: Usamos Node.js 22 en su versión ligera (alpine) para que la imagen pese menos.
  • WORKDIR /app: Carpeta dentro del contenedor donde se copiarán los archivos del proyecto.
  • COPY package*.json ./: Copia los archivos package.json y package-lock.json para instalar dependencias.
  • RUN npm install: Instala las dependencias de React y Vite.
  • COPY . .: Copia todo el código del frontend al contenedor.
  • RUN npm run build: Genera la versión optimizada para producción.
  • EXPOSE 5173: Expone el puerto donde Vite servirá la aplicación.
  • CMD: Ejecuta el comando para iniciar el servidor de desarrollo de Vite, accesible desde cualquier dispositivo en la red.

💡 Más adelante conectaremos este contenedor con el backend y la base de datos en el archivo docker-compose.yml para que todo funcione con un solo comando.


📌7. Crear el Dockerfile del Backend

Ahora vamos a preparar el Dockerfile para el backend. Este archivo define cómo se construirá la imagen de nuestro servidor con Express.

Archivo backend/Dockerfile

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]

Explicación línea por línea

  • FROM node:22-alpine: Usamos Node.js 22 en su versión ligera para reducir el peso de la imagen.
  • WORKDIR /app: Define la carpeta dentro del contenedor donde vivirá nuestro código.
  • COPY package*.json ./: Copia la configuración del proyecto para instalar dependencias.
  • RUN npm install: Instala las dependencias necesarias (Express, MySQL2, CORS, etc.).
  • COPY . .: Copia todo el código del backend dentro del contenedor.
  • EXPOSE 3000: Expone el puerto 3000, que será donde escuchará el servidor Express.
  • CMD ["npm", "run", "dev"]: Ejecuta el backend en modo desarrollo con nodemon.

💡 En este punto ya tenemos listo el backend para ejecutarse dentro de un contenedor de Docker. Más adelante lo conectaremos a la base de datos MySQL usando Docker Compose.


📌8. Configurar docker-compose.yml

Este archivo nos permite levantar todo el proyecto con un solo comando. Con docker-compose vamos a ejecutar el frontend, backend y la base de datos MySQL, todo en contenedores conectados entre sí.

Archivo docker-compose.yml (en la raíz del proyecto)

version: "3.8"
services:
  frontend:
    build: ./frontend
    ports:
      - "5173:5173"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    depends_on:
      - backend

  backend:
    build: ./backend
    ports:
      - "3000:3000"
    volumes:
      - ./backend:/app
      - /app/node_modules
    depends_on:
      - db

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: miapp
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql

volumes:
  mysql_data:

Explicación

  • version: "3.8" → Versión del formato de Docker Compose que usaremos.
  • services → Lista de servicios (contenedores) que vamos a crear.
  • frontend → Construye la imagen desde la carpeta frontend, expone el puerto 5173 y depende del backend.
  • backend → Construye la imagen desde la carpeta backend, expone el puerto 3000 y depende de la base de datos.
  • db → Usa la imagen oficial de MySQL 8.0, crea la base de datos miapp con usuario root y guarda los datos en un volumen para que no se pierdan.
  • volumesmysql_data es un volumen persistente para MySQL.

💡 Con este archivo listo, podremos levantar todo el sistema con un solo comando:

docker compose up --build

Esto construirá las imágenes, creará los contenedores y dejará el proyecto listo para usarse en:

  • Frontend: http://localhost:5173
  • Backend: http://localhost:3000
  • MySQL: puerto 3306 para conexiones externas

📌9. Levantar todo con Docker

Ahora que tenemos todos los archivos listos, podemos iniciar el proyecto completo (frontend, backend y base de datos) con un solo comando usando docker compose.

docker compose up --build

Explicación del comando

  • docker compose → Ejecuta el archivo docker-compose.yml que está en la raíz del proyecto.
  • up → Levanta todos los servicios definidos (contenedores) y los conecta en la misma red interna.
  • --build → Fuerza la reconstrucción de las imágenes para incluir cualquier cambio en el código.

Mientras se ejecuta, verás en la terminal cómo se construyen las imágenes, se crean los contenedores y se muestran los logs en tiempo real.

Acceso a la aplicación

Una vez que el comando termine de levantar todo, podrás acceder a:

  • Frontend: http://localhost:5173
  • Backend: http://localhost:3000
  • MySQL: Puerto 3306 disponible para conexiones desde tu gestor de base de datos favorito.

💡 Para detener los contenedores, presiona CTRL + C en la terminal y luego ejecuta:

docker compose down

📌 10. Probar

Ahora que todo está funcionando, es momento de verificar que cada servicio esté activo y conectado correctamente:

  • Frontend: http://localhost:5173 → Interfaz de usuario creada con Vite y React.
  • Backend: http://localhost:3000 → API creada con Express.
  • Base de datos: Puerto 3306 → Accede mediante un cliente MySQL como MySQL Workbench o phpMyAdmin (si lo instalas).

Si todo carga correctamente:

  1. En Frontend deberías poder ver la aplicación inicial creada con Vite.
  2. En Backend deberías obtener el mensaje Servidor funcionando 🚀 si visitas la raíz.
  3. En Base de datos ya tienes creada automáticamente la base miapp.

🎉 ¡Listo! Ahora tienes un proyecto Fullstack completamente funcional dentro de contenedores con Vite + Express + MySQL gracias a Docker Compose.

🔥 Bonus técnico: ¿Qué pasa si el backend se cae por MySQL?

En algunos entornos, especialmente en Docker sobre Windows, puede ocurrir que el backend intente conectarse a MySQL antes de que esté listo, generando el error ECONNREFUSED.

Esto sucede porque Docker Compose no espera a que MySQL esté completamente inicializado, solo a que el contenedor esté corriendo.

💡 Solución recomendada: usar reconexión automática en el backend con mysql2/promise, o herramientas como wait-for-it.sh para que el backend espere antes de arrancar.

En mi caso, el error se resolvió automáticamente al reiniciar el stack, por lo que no fue necesario modificar el código. Pero es importante tenerlo en cuenta si el problema persiste en otros entornos.

Publicar un comentario

0 Comentarios