skip to Main Content

I have a project with docker and bun, a website and an api, both are compiled with bun. The website is with Astro and the API is just Bun.
The problem I have is that it does not detect the changes in the files no matter how much I have tried every way I have found on the internet.
It should be noted that if I start the projects independently, they do detect the hot reload.

I will leave the files that I use for this project:

Structure:

.
├── README.md
├── api
│   ├── Dockerfile
│   ├── bun.lockb
│   ├── package.json
│   ├── src
│   │   └── index.ts
│   └── tsconfig.json
├── docker-compose.yml
├── nginx
│   └── nginx.conf
└── web
    ├── Dockerfile
    ├── README.md
    ├── astro.config.mjs
    ├── bun.lockb
    ├── index.html
    ├── package.json
    ├── public
    │   └── favicon.svg
    ├── src
    │   ├── components
    │   │   ├── Card.astro
    │   │   ├── Post.vue
    │   │   ├── Posts.vue
    │   │   ├── atoms
    │   │   │   └── Button.vue
    │   │   ├── molecules
    │   │   ├── organisms
    │   │   └── templates
    │   ├── env.d.ts
    │   ├── layouts
    │   │   └── Layout.astro
    │   ├── pages
    │   │   ├── api
    │   │   │   └── posts
    │   │   │       ├── [id].ts
    │   │   │       └── index.ts
    │   │   ├── index.astro
    │   │   └── posts
    │   │       ├── [id].astro
    │   │       └── index.astro
    │   └── utils
    ├── tsconfig.json
    └── tsconfig.node.json

17 directories, 28 files
# Path: docker-compose.yml

version: '3.8'

networks:
  cv_network:
    driver: bridge

volumes:
  web_node_modules:
  api_node_modules:

services:
  web:
    container_name: cv_web
    build: web
    image: web
    restart: "no"
    networks:
      - cv_network
    volumes:
      - ./web/:/app
      - web_node_modules:/app/node_modules
    ports:
      - 3000:3000
    env_file:
      - project.env

  api:
    container_name: cv_api
    build: api
    image: api
    restart: "no"
    networks:
      - cv_network
    volumes:
      - ./api/:/app
      - api_node_modules:/app/node_modules
    ports:
      - 8000:8000

  nginx:
    container_name: cv_nginx
    image: nginx:1.25.2-alpine-slim
    networks:
      - cv_network
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - web
      - api
    ports:
      - 7800:7800
# Path: api/Dockerfile

FROM oven/bun:1.0.3-slim

WORKDIR /api

COPY package*.json bun.lockb ./
RUN bun install
COPY . .

CMD [ "bun", "run", "dev" ]

Path: api/package.json

{
  "name": "bun_app",
  "version": "1.0.50",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "dev": "bun --watch run src/index.ts"
  },
  "dependencies": {
  },
  "devDependencies": {
    "bun-types": "1.0.3"
  },
  "module": "src/index.js"
}

// Path: api/src/index.ts

import { type Serve } from "bun";

type Middleware = (req: any, res: any, next: () => void) => void;
type RequestHandler = (req: any, res: any) => Response;

const loggerMiddleware: Middleware = (req, res, next) => {
  const url = new URL(req.url);
  const method = req.method;
  const currentTime = new Date().toISOString();

  console.log(`[${currentTime}] ${method} - ${url.pathname}`);
  next();
};

class Router {
  routes: { [method: string]: { [path: string]: RequestHandler } } = {};

  get(path: string, handler: RequestHandler) {
    this.addRoute("GET", path, handler);
  }

  post(path: string, handler: RequestHandler) {
    this.addRoute("POST", path, handler);
  }

  put(path: string, handler: RequestHandler) {
    this.addRoute("PUT", path, handler);
  }

  private addRoute(method: string, path: string, handler: RequestHandler) {
    if (!this.routes[method]) {
      this.routes[method] = {};
    }
    this.routes[method][path] = handler;
  }
}

class Server {
  middlewares: Middleware[] = [];
  routes: { [method: string]: { [path: string]: RequestHandler } } = {};

  use(middleware: Middleware) {
    this.middlewares.push(middleware);
  }

  get(path: string, handler: RequestHandler) {
    this.addRoute("GET", path, handler);
  }

  post(path: string, handler: RequestHandler) {
    this.addRoute("POST", path, handler);
  }

  // Agregar más métodos HTTP según sea necesario

  private addRoute(method: string, path: string, handler: RequestHandler) {
    if (!this.routes[method]) {
      this.routes[method] = {};
    }
    this.routes[method][path] = handler;
  }

  useRouter(path: string, router: Router) {
    for (const [method, routes] of Object.entries(router.routes)) {
      for (const [routePath, handler] of Object.entries(routes)) {
        const fullPath = path + routePath;
        this.addRoute(method, fullPath, handler);
      }
    }
  }

  async fetch(request: Request, server: Server): Promise<Response> {
    try {
      if (!request || !server) {
        console.error("Request o Server no definidos");
        return new Response("Internal Server Error", { status: 500 });
      }

      const url = new URL(request.url);
      const pathname = url.pathname;
      const method = request.method;

      let response: Response;

      // Ejecutamos los middlewares en orden
      for (const middleware of this.middlewares) {
        middleware(request, response, () => {});
      }

      if (this.routes[method] && this.routes[method][pathname]) {
        const handler = this.routes[method][pathname];
        response = handler(request, new Response(""));
      } else {
        throw new Error("Route not found"); // Lanza un error si la ruta no se encuentra
      }

      return response;
    } catch (error) {
      console.error(`Error: ${error.message}`);
      return new Response(error.message, { status: 500 });
    }
  }
}

const app = new Server();

app.use(loggerMiddleware);

// Agregar middlewares
app.use((req, res, next) => {
  console.log("Middleware 1");
  next();
});

// Agregar rutas
app.get("/", (req, res) => {
  res = new Response("Hello, world!", { status: 200 });
  return res;
});

const router = new Router();

router.get("", (req, res) => {
  return new Response(JSON.stringify({ message: "aaaaaaaaaa" }), {
    status: 200,
  });
});

router.post("", (req, res) => {
  return new Response(JSON.stringify({ message: "Router POST succeed" }), {
    status: 200,
  });
});

router.put("", (req, res) => {
  return new Response(JSON.stringify({ message: "Router PUT succeed" }), {
    status: 200,
  });
});

app.useRouter("/test", router);

// Iniciar el servidor
export default {
  port: 8000,
  fetch(req) {
    return app.fetch(req, app);
  },
  error(error: Error) {
    return new Response(`<pre>${error}n${error.stack}</pre>`, {
      headers: {
        "Content-Type": "text/html",
      },
    });
  },
} satisfies Serve;

# Path: nginx/nginx.conf

worker_processes 1;

events {
  worker_connections 1024;
}

http {
  # proxy_cache_path /var/cache/nginx keys_zone=astro_ssg_cache:1m inactive=5m max_size=512m;

  upstream web {
    server web:3000;
  }

  upstream api {
    server api:8000;
  }

  server {
    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;
    gzip_min_length 1000;

    listen 7800;
    server_name localhost;

    location / {
      proxy_pass http://web;
      proxy_http_version 1.1;
      # proxy_cache astro_ssg_cache;
      # proxy_cache_key $uri;
      # proxy_cache_valid 5m;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "Upgrade";
      proxy_set_header Host $host;
      proxy_redirect off;
    }
  }

  # Para la API
  server {
    listen 7800;
    server_name api.localhost;
    location / {
      proxy_pass http://api;  # Suponiendo que "api" es el nombre del servicio en docker-compose.yml
      # ... (otros ajustes de proxy aquí)
    }
  }

  # Para el panel de administración
  server {
    listen 7800;
    server_name admin.localhost;
    location / {
      proxy_pass http://web;  # Asumiendo que "admin" es otro servicio en docker-compose.yml
      # ... (otros ajustes de proxy aquí)
    }
  }

  # Para otros subdominios dinámicos
  server {
    listen 7800;
    server_name ~^(?<subdomain>.+).localhost$;
    location / {
      proxy_pass http://web;  # Redirige todos los demás subdominios al servicio "web"
      # ... (otros ajustes de proxy aquí)
    }
  }
}

Link to repo

  • SO: Windows 11
  • WSL2:
  • Distributor ID: Ubuntu
  • Description: Ubuntu 20.04.6 LTS
  • Release: 20.04
  • Codename: focal

2

Answers


  1. Chosen as BEST ANSWER

    Solution to the Hot Reload Issue

    After reviewing the files, I found the differences that were causing the Hot Reload issue. Here are the necessary changes to make everything work as expected.

    docker-compose.yml

    First, in the docker-compose.yml file, I needed to update the volume paths to match the working directories in the containers.

    Before:

    volumes:
      - ./web/:/app
      - web_node_modules:/app/node_modules
    

    After:

    volumes:
      - ./web/:/web
      - web_node_modules:/web/node_modules
    

    Dockerfile in web and api

    package.json in api

    Finally, in the package.json in the api folder, change the dev script to use the --hot option.

    Before:

    "dev": "bun --watch run src/index.ts"
    

    After:

    "dev": "bun --hot src/index.ts"
    

    Summary

    1. I updated the volume paths in docker-compose.yml.
    2. Modify the dev script in api/package.json to use --hot.

    With these changes, Hot Reload works as expected. 😎

    Thanks to Revolucion for Monica who gave me the idea in this comment on SO.


  2. Can you change the –watch with –hot. Hot will soft reload the code.

    "scripts": {
       "start": "bun ./bin/www.js",
       "dev": "bun --hot ./bin/www.js"
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search