Search code examples
dockerdocker-composenuxt3.js

Nuxt 3 gives a 500 fetch failed error on Docker and Docker Compose


The application works correctly when I run it locally, but if I try to dockerize it, I get a 500 fetch failed error with no logs. I also tried running the application with node --trace-warnings but no errors appear. The project is a pnpm monorepo, and the folder structure is

apps
│
├── backend
│   ├── Dockerfile
│   └── package.json
│
├── frontend
│   ├── Dockerfile
|   ├── nuxt.config.ts
│   └── package.json
│
├── docker-compose.yaml
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml

Here is the apps/frontend/Dockerfile

###################
# STAGE 1: BUILD
###################

FROM node:20-alpine AS build

# Set working directory inside the image
WORKDIR /app

# Copy application dependency manifests to the container image.
COPY --chown=node:node ./package.json ./pnpm-lock.yaml ./pnpm-workspace.yaml ./
COPY --chown=node:node ./apps/frontend/package.json ./apps/frontend/package.json

# Install pnpm globally
RUN npm install -g pnpm

# Install app dependencies using the `pnpm install --frozen-lockfile`
RUN pnpm install --frozen-lockfile

# Bundle app source
COPY --chown=node:node ./apps/frontend ./apps/frontend/

# Build the Nuxt.js application
RUN pnpm --filter=@myapp/frontend build

# Use the node user from the image (instead of the root user)
USER node

###################
# STAGE 2: PRODUCTION
###################

FROM node:20-alpine AS production

# Set working directory inside the image
WORKDIR /app

# Copy compiled static files from the build stage (Stage 1)
COPY --chown=node:node --from=build /app/apps/frontend/.output/  ./

# Port and host for the nitro
ENV NITRO_HOST=0.0.0.0
ENV NITRO_PORT=3000

# Expose the port the app runs on
EXPOSE 3000

# Use the node user from the image (instead of the root user)
USER node

# Default command to start Nuxt
CMD [ "node", "/app/server/index.mjs" ]

Here is the docker-compose.yaml

version: '3.8'

services:
  frontend:
    container_name: frontend
    build:
      context: .
      dockerfile: apps/frontend/Dockerfile
    ports:
      - '3000:3000'
    networks:
      - mynetwork
    depends_on:
      - backend

  backend:
    container_name: backend
    build:
      context: .
      dockerfile: apps/backend/Dockerfile
    ports:
      - '3001:3001'
    depends_on:
      - db
    networks:
      - mynetwork
    environment:
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - POSTGRES_USERNAME=postgres
      - POSTGRES_PASSWORD=password
      - POSTGRES_DATABASE=database
      - SERVER_ADDRESS=0.0.0.0
      - SERVER_PORT=3001

  db:
    container_name: postgres
    image: postgres:13
    ports:
      - '5432:5432'
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: database
    networks:
      - mynetwork
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

networks:
  mynetwork:
    driver: bridge

Here is the nuxt.config.ts

export default defineNuxtConfig({
    devtools: { enabled: true },
    modules: [
        "@nuxtjs/apollo",
        "@nuxtjs/tailwindcss",
        "@nuxtjs/color-mode",
        "@nuxt/icon",
        "@element-plus/nuxt",
        "@nuxt/image",
    ],
    plugins: [{ src: "~/plugins/vue-toastification.ts", mode: "client" }],
    apollo: {
        clients: {
            default: {
                httpEndpoint: `${ process.env.SERVER_URL ?? "http://127.0.0.1:3001/graphql"}`,
                inMemoryCacheOptions: {
                    addTypename: false,
                },
                defaultOptions: {
                    watchQuery: {
                        fetchPolicy: "no-cache",
                    },
                    query: {
                        fetchPolicy: "no-cache",
                    },
                },
            },
        },
    },
    css: ["@fontsource-variable/inter/index.css"],
});

This is all I have. enter image description here

UPDATE

I also tried setting the frontend service environment variable SERVER_URL with the name of the backend service container, but I still get the same error.

services:
  frontend:
    container_name: frontend
    build:
      context: .
      dockerfile: apps/frontend/Dockerfile
    ports:
      - '3000:3000'
    networks:
      - mynetwork
    depends_on:
      - backend
    environment:
      - SERVER_URL=backend:3001/graphql

  backend:
    container_name: backend
    build:
      context: .
      dockerfile: apps/backend/Dockerfile

Solution

  • In the end, I found the problem: you need to set two different URLs for GraphQL in nuxt.config.js, one for the server-side and one for the client-side in Nuxt, so that the server-side URL points to the container while the client-side URL points to the URL exposed by the container.

    Here is the updated nuxt.config.js where we have defined httpEndpoint (for the server-side) and browserHttpEndpoint (for the client-side):

    export default defineNuxtConfig({
        ...
        apollo: {
            clients: {
                default: {
                    httpEndpoint: `${ process.env.SERVER_URL ?? "http://127.0.0.1:3001/graphql"}`,
                    browserHttpEndpoint: "http://127.0.0.1:3001/graphql",
                },
            },
        },
        ...
    
    });
    
    

    Here is the updated docker-compose.yaml where we pass the argument SERVER_URL used in the build stage of the Nuxt.js project.

    services:
      frontend:
        container_name: frontend
        build:
          context: .
          dockerfile: apps/frontend/Dockerfile
          args:
            - SERVER_URL=http://backend:3001/graphql
        ports:
          - '3000:3000'
        networks:
          - mynetwork
        depends_on:
          - backend
    

    In the Dockerfile of the Nuxt.js project, we need to add the environment variable SERVER_URL in the build stage before running the build command

    ...
    
    # Custom server URL
    ARG SERVER_URL
    ENV SERVER_URL=$SERVER_URL
    
    # Build the Nuxt.js application
    RUN pnpm --filter=@weroad/frontend build
    
    ...