# Utiliza contenedores para el desarrollo con Node.js


## Requisitos previos

Completa la sección de [Contenerizar una aplicación Node.js](/guides/nodejs/develop/containerize/).

## Descripción general

En esta sección, aprenderás a configurar un entorno de desarrollo para tu aplicación contenerizada. Esto incluye:

- Agregar una base de datos local y persistir los datos.
- Configurar tu contenedor para ejecutar un entorno de desarrollo.
- Depurar tu aplicación contenerizada.

## Agrega una base de datos local y persiste los datos

La aplicación utiliza PostgreSQL para la persistencia de datos. Agrega un servicio de base de datos a tu configuración de Docker Compose.

### Agrega el servicio de base de datos a Docker Compose

Si aún no has creado un archivo `compose.yml` en la sección anterior, o si necesitas agregar el servicio de base de datos, update tu archivo `compose.yml` para incluir el servicio de la base de datos PostgreSQL:

```yaml
services:
  # ... servicios de aplicación existentes ...

  # ========================================
  # Servicio de base de datos PostgreSQL
  # ========================================
  db:
    image: postgres:18-alpine
    container_name: todoapp-db
    environment:
      POSTGRES_DB: '${POSTGRES_DB:-todoapp}'
      POSTGRES_USER: '${POSTGRES_USER:-todoapp}'
      POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
    volumes:
      - postgres_data:/var/lib/postgresql
    ports:
      - '${DB_PORT:-5432}:5432'
    restart: unless-stopped
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-todoapp} -d ${POSTGRES_DB:-todoapp}']
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 5s
    networks:
      - todoapp-network

# ========================================
# Configuración de volúmenes
# ========================================
volumes:
  postgres_data:
    name: todoapp-postgres-data
    driver: local

# ========================================
# Configuración de red
# ========================================
networks:
  todoapp-network:
    name: todoapp-network
    driver: bridge
```

### Actualiza el servicio de tu aplicación

Asegúrate de que el servicio de tu aplicación en `compose.yml` esté configurado para conectarse a la base de datos:

```yaml {hl_lines="18-20,42-44",collapse=true,title=compose.yml}
services:
  app-dev:
    build:
      context: .
      dockerfile: Dockerfile
      target: development
    container_name: todoapp-dev
    ports:
      - '${APP_PORT:-3000}:3000' # Servidor API
      - '${VITE_PORT:-5173}:5173' # Servidor de desarrollo de Vite
      - '${DEBUG_PORT:-9229}:9229' # Depurador de Node.js
    environment:
      NODE_ENV: development
      DOCKER_ENV: 'true'
      POSTGRES_HOST: db
      POSTGRES_PORT: 5432
      POSTGRES_DB: todoapp
      POSTGRES_USER: todoapp
      POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
      ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:5173}'
    volumes:
      - ./src:/app/src:ro
      - ./package.json:/app/package.json
      - ./vite.config.ts:/app/vite.config.ts:ro
      - ./tailwind.config.js:/app/tailwind.config.js:ro
      - ./postcss.config.js:/app/postcss.config.js:ro
    depends_on:
      db:
        condition: service_healthy
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
          ignore:
            - '**/*.test.*'
            - '**/__tests__/**'
        - action: rebuild
          path: ./package.json
        - action: sync
          path: ./vite.config.ts
          target: /app/vite.config.ts
        - action: sync
          path: ./tailwind.config.js
          target: /app/tailwind.config.js
        - action: sync
          path: ./postcss.config.js
          target: /app/postcss.config.js
    restart: unless-stopped
    networks:
      - todoapp-network

  db:
    image: postgres:18-alpine
    container_name: todoapp-db
    environment:
      POSTGRES_DB: '${POSTGRES_DB:-todoapp}'
      POSTGRES_USER: '${POSTGRES_USER:-todoapp}'
      POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
    volumes:
      - postgres_data:/var/lib/postgresql
    ports:
      - '${DB_PORT:-5432}:5432'
    restart: unless-stopped
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-todoapp} -d ${POSTGRES_DB:-todoapp}']
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 5s
    networks:
      - todoapp-network

volumes:
  postgres_data:
    name: todoapp-postgres-data
    driver: local

networks:
  todoapp-network:
    name: todoapp-network
    driver: bridge
```

1. La configuración de la base de datos PostgreSQL se gestiona automáticamente por la aplicación. La base de datos se crea e inicializa cuando la aplicación se inicia, y los datos persisten mediante el volumen `postgres_data`.

2. Configura tu entorno copiando el archivo de ejemplo:

   ```console
   $ cp .env.example .env
   ```

   Actualiza el archivo `.env` con tus preferencias:

   ```env
   # Configuración de la aplicación
   NODE_ENV=development
   APP_PORT=3000
   VITE_PORT=5173
   DEBUG_PORT=9230

   # Configuración de la base de datos
   POSTGRES_HOST=db
   POSTGRES_PORT=5432
   POSTGRES_DB=todoapp
   POSTGRES_USER=todoapp
   POSTGRES_PASSWORD=todoapp_password

   # Configuración de seguridad
   ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
   ```

3. Ejecuta el siguiente comando para iniciar tu aplicación en modo de desarrollo:

   ```console
   $ docker compose up app-dev --build
   ```

4. Abre un navegador y verifica que la aplicación se esté ejecutando en [http://localhost:5173](http://localhost:5173) para el frontend o en [http://localhost:3000](http://localhost:3000) para la API. El frontend de React es servido por el servidor de desarrollo de Vite en el puerto 5173, y las llamadas a la API se reenvían al servidor Express en el puerto 3000.

5. Agrega algunos elementos a la lista de tareas para probar la persistencia de datos.

6. Después de agregar algunos elementos a la lista de tareas, presiona `CTRL + C` en la terminal para detener la aplicación.

7. Ejecuta la aplicación de nuevo:
   ```console
   $ docker compose up app-dev
   ```

8. Actualiza [http://localhost:5173](http://localhost:5173) en tu navegador y verifica que los elementos de la lista de tareas persisten, incluso después de que los contenedores hayan sido eliminados y ejecutados de nuevo.

## Configura y ejecuta un contenedor de desarrollo

Puedes utilizar un montaje de tipo bind (bind mount) para montar tu código fuente en el contenedor. De este modo, el contenedor podrá ver los cambios que realices en el código de forma inmediata, en cuanto guardes un archivo. Esto significa que puedes ejecutar procesos, como `nodemon`, dentro del contenedor para vigilar los cambios en el sistema de archivos y responder a ellos. Para conocer más sobre los montajes de tipo bind, consulta la [descripción general de almacenamiento](/engine/storage/).

Además de agregar un montaje bind, puedes configurar tu Dockerfile y tu archivo `compose.yaml` para instalar dependencias de desarrollo y ejecutar herramientas de desarrollo.

### Actualiza tu Dockerfile para el desarrollo

Tu Dockerfile debe estar configurado como una construcción multi-etapa con etapas independientes para desarrollo, producción y pruebas. Si seguiste la sección anterior, tu Dockerfile ya incluye una etapa de desarrollo que cuenta con todas las dependencias de desarrollo y ejecuta la aplicación con la recarga en caliente habilitada.

Esta es la etapa de desarrollo de tu Dockerfile multi-etapa:

```dockerfile {hl_lines="5-26",collapse=true,title=Dockerfile}
# ========================================
# Etapa de desarrollo
# ========================================
FROM build-deps AS development

# Establecer el entorno
ENV NODE_ENV=development \
    NPM_CONFIG_LOGLEVEL=warn

# Copiar archivos fuente
COPY . .

# Asegurar que todos los directorios tengan los permisos adecuados
RUN mkdir -p /app/node_modules/.vite && \
    chown -R nodejs:nodejs /app && \
    chmod -R 755 /app

# Cambiar a usuario no root
USER nodejs

# Exponer puertos
EXPOSE 3000 5173 9229

# Iniciar el servidor de desarrollo
CMD ["npm", "run", "dev:docker"]
```

La etapa de desarrollo:

- Instala todas las dependencias, incluidas las de desarrollo (dev dependencies).
- Expone puertos para el servidor API (3000), el servidor de desarrollo de Vite (5173) y el depurador de Node.js (9229).
- Ejecuta `npm run dev`, que inicia tanto el servidor Express como el servidor de desarrollo de Vite de forma concurrente.
- Incluye comprobaciones de estado para monitorear el estado del contenedor.

A continuación, tendrás que actualizar tu archivo de Compose para utilizar esta nueva etapa.

### Actualiza tu archivo de Compose para el desarrollo

Actualiza tu archivo `compose.yml` para ejecutar la etapa de desarrollo con montajes bind para la recarga en caliente:

```yaml {hl_lines=[5,8-10,20-27],collapse=true,title=compose.yml}
services:
  app-dev:
    build:
      context: .
      dockerfile: Dockerfile
      target: development
    container_name: todoapp-dev
    ports:
      - '${APP_PORT:-3000}:3000' # Servidor API
      - '${VITE_PORT:-5173}:5173' # Servidor de desarrollo de Vite
      - '${DEBUG_PORT:-9229}:9229' # Depurador de Node.js
    environment:
      NODE_ENV: development
      DOCKER_ENV: 'true'
      POSTGRES_HOST: db
      POSTGRES_PORT: 5432
      POSTGRES_DB: todoapp
      POSTGRES_USER: todoapp
      POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}'
      ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:5173}'
    volumes:
      - ./src:/app/src:ro
      - ./package.json:/app/package.json
      - ./vite.config.ts:/app/vite.config.ts:ro
      - ./tailwind.config.js:/app/tailwind.config.js:ro
      - ./postcss.config.js:/app/postcss.config.js:ro
    depends_on:
      db:
        condition: service_healthy
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
          ignore:
            - '**/*.test.*'
            - '**/__tests__/**'
        - action: rebuild
          path: ./package.json
        - action: sync
          path: ./vite.config.ts
          target: /app/vite.config.ts
        - action: sync
          path: ./tailwind.config.js
          target: /app/tailwind.config.js
        - action: sync
          path: ./postcss.config.js
          target: /app/postcss.config.js
    restart: unless-stopped
    networks:
      - todoapp-network
```

Características clave de la configuración de desarrollo:

- **Exposición multipuerto**: Servidor API (3000), servidor de desarrollo de Vite (5173) y depurador (9229).
- **Montajes bind completos**: Código fuente, archivos de configuración y archivos de paquete para recarga en caliente.
- **Variables de entorno**: Configurables a través del archivo `.env` o valores por defecto.
- **Base de datos PostgreSQL**: Base de datos lista para producción con almacenamiento persistente.
- **Docker Compose watch**: Sincronización automática de archivos y reconstrucción de contenedores.
- **Comprobaciones de estado**: Monitoreo del estado de salud de la base de datos con gestión automática de dependencias.

### Ejecuta tu contenedor de desarrollo y depura tu aplicación

Ejecuta el siguiente comando para iniciar tu aplicación con la configuración de desarrollo:

```console
$ docker compose up app-dev --build
```

O bien, con observación de archivos para actualizaciones automáticas:

```console
$ docker compose up app-dev --watch
```

Para el desarrollo local sin Docker:

```console
$ npm run dev:with-db
```

O inicia los servicios por separado:

```console
$ npm run db:start    # Iniciar el contenedor de PostgreSQL
$ npm run dev         # Iniciar tanto el servidor como el cliente
```

### Uso de Task Runner (alternativa)

El proyecto incluye un archivo `Taskfile.yml` para flujos de trabajo avanzados:

```console
# Desarrollo
$ task dev              # Iniciar el entorno de desarrollo
$ task dev:build        # Construir la imagen de desarrollo
$ task dev:run          # Ejecutar el contenedor de desarrollo

# Producción
$ task build            # Construir la imagen de producción
$ task run              # Ejecutar el contenedor de producción
$ task build-run        # Construir y ejecutar en un solo paso

# Pruebas
$ task test             # Ejecutar todas las pruebas
$ task test:unit        # Ejecutar pruebas unitarias con cobertura
$ task test:lint        # Ejecutar análisis estático (linting)

# Kubernetes
$ task k8s:deploy       # Desplegar en Kubernetes
$ task k8s:status       # Comprobar el estado del despliegue
$ task k8s:logs         # Ver los logs de los pods

# Utilidades
$ task clean            # Limpiar contenedores e imágenes
$ task health           # Comprobar el estado de salud de la aplicación
$ task logs             # Ver los logs del contenedor
```

La aplicación se iniciará con el servidor API Express y el servidor de desarrollo de Vite:

- **Servidor API**: [http://localhost:3000](http://localhost:3000) - Backend de Express.js con API REST
- **Frontend**: [http://localhost:5173](http://localhost:5173) - Servidor de desarrollo de Vite con reemplazo de módulos en caliente (hot module replacement)
- **Comprobación de estado (Health Check)**: [http://localhost:3000/health](http://localhost:3000/health) - Estado de salud de la aplicación

Cualquier cambio en los archivos fuente de la aplicación en tu máquina local se reflejará inmediatamente en el contenedor en ejecución gracias a los montajes bind.

Intenta realizar un cambio para probar la recarga en caliente:

1. Abre `src/client/components/TodoApp.tsx` en un IDE o editor de texto.
2. Actualiza el texto del encabezado principal:

    ```diff
    - <h1 className="text-3xl font-bold text-gray-900 mb-8">
    -   Modern Todo App
    - </h1>
    + <h1 className="text-3xl font-bold text-gray-900 mb-8">
    +   My Todo App
    + </h1>
    ```

3. Guarda el archivo y el servidor de desarrollo de Vite recargará la página automáticamente con tus cambios.

**Soporte de depuración:**

Puedes conectar un depurador a tu aplicación en el puerto 9229. El inspector de Node.js está habilitado con `--inspect=0.0.0.0:9230` en el script de desarrollo (`dev:server`).

### Configuración del depurador en VS Code

1. Crea una configuración de lanzamiento en `.vscode/launch.json`:

    ```json
    {
      "version": "0.2.0",
      "configurations": [
        {
          "name": "Adjuntar al contenedor Docker",
          "type": "node",
          "request": "attach",
          "port": 9229,
          "address": "localhost",
          "localRoot": "${workspaceFolder}",
          "remoteRoot": "/app",
          "protocol": "inspector",
          "restart": true,
          "sourceMaps": true,
          "skipFiles": ["<node_internals>/**"]
        }
      ]
    }
    ```

2. Inicia tu contenedor de desarrollo:

    ```console
    docker compose up app-dev --build
    ```

3. Adjunta el depurador:
   - Abre VS Code.
   - En el panel de depuración (Ctrl/Cmd + Shift + D), selecciona **Adjuntar al contenedor Docker** en el menú desplegable.
   - Selecciona el botón verde de reproducción o presiona F5.

### Chrome DevTools (alternativa)

También puedes usar Chrome DevTools para la depuración:

1. Inicia tu contenedor (si aún no se está ejecutando):

    ```console
    docker compose up app-dev --build
    ```

2. Abre Chrome y ve a `chrome://inspect`.

3. En la opción **Configure** (Configurar), agrega:

    ```text
    localhost:9229
    ```

4. Cuando aparezca tu objetivo (target) de Node.js, selecciona **inspect**.

### Detalles de la configuración de depuración

La configuración de depuración:

- **Puerto del contenedor**: 9230 (puerto interno del depurador)
- **Puerto del host**: 9229 (puerto externo mapeado)
- **Script**: `tsx watch --inspect=0.0.0.0:9230 src/server/index.ts`

El depurador escucha en todas las interfaces (`0.0.0.0`) dentro del contenedor en el puerto 9230 y es accesible en el puerto 9229 desde tu máquina host.

### Solución de problemas de conexión del depurador

Si el depurador no se conecta:

1. Comprueba si el contenedor se está ejecutando:

    ```console
    docker ps
    ```

2. Comprueba si el puerto está expuesto:

    ```console
    docker port todoapp-dev
    ```

3. Comprueba los logs del contenedor:

    ```console
    docker compose logs app-dev
    ```

    Deberías ver un mensaje como:

    ```text
    Debugger listening on ws://0.0.0.0:9230/...
    ```

Ahora puedes establecer puntos de interrupción (breakpoints) en tus archivos fuente de TypeScript y depurar tu aplicación Node.js contenerizada.

Para obtener más detalles sobre la depuración en Node.js, consulta la [documentación oficial de Node.js](https://nodejs.org/en/docs/guides/debugging-getting-started).

## Resumen

Has configurado tu archivo de Compose con una base de datos PostgreSQL y persistencia de datos. También creaste un Dockerfile multi-etapa y configuraste montajes bind para el desarrollo.

Información relacionada:

- [Elemento de nivel superior volumes en Compose](/reference/compose-file/volumes/)
- [Elemento de nivel superior services en Compose](/reference/compose-file/services/)
- [Construcciones multi-etapa](/build/building/multi-stage/)

## Próximos pasos

En la siguiente sección, aprenderás cómo ejecutar pruebas unitarias utilizando Docker.

