basename and homepage not working as expected to serve /static/ files in development

12/19/2019

Not sure if this is a Kubernetes, ingress-nginx, or ReactJS (create-react-app) issue...

Project Structure

new-app/
  client/
    src/
      App.js
      index.js
      Test.js
    package.json
  k8s/
    client.yaml
    ingress.yaml
  server/
  skaffold.yaml

Issue

  • ReactJS front-end should be running at 192.168.64.5/client when the cluster is spun up with Skaffold
  • Navigate to 192.168.64.5/client and blank screen
  • Developer Console shows:

enter image description here enter image description here

Basically, it is trying to serve static files from /static, but I need them to come from /client/static

Propsed Solutions

Assuming this is a ReactJS issue, the solutions have been the following:

None seem to work in my case. Still shows assets trying to be served from /static instead of /client/static.

ReactJS App Code

// App.js

import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import './App.css';

import Test from './Test';

const App = () => {
  return (
    <BrowserRouter
      basename='/client'>
        <>
          <Route exact path='/' component={Test} />
        </>
    </BrowserRouter>

  );
}

export default App;
// Test.js

import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';

const Test = () => {

  const [data, setData] = useState('');

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios('/api/auth/test/');
      setData(result.data);
    };
    fetchData();
  }, []);

  return (
    <div className='App'>
    <header className='App-header'>
      <img src={logo} className='App-logo' alt='logo' />
      <p>
        Edit <code>src/App.js</code> and save to reload!
      </p>
      <p>
        {data}
      </p>
      <a
        className='App-link'
        href='https://reactjs.org'
        target='_blank'
        rel='noopener noreferrer'
      >
        Learn React
      </a>
    </header>
    </div>    
  )

};

export default Test;
{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "homepage": "/client",
  "dependencies": {
    "axios": "^0.19.0",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-router-dom": "^5.1.2",
    "react-scripts": "3.2.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Kubernetes/Skaffold and Docker Manifests

# Dockerfile.dev

FROM node:alpine
EXPOSE 3000
WORKDIR '/app'
COPY ./client/package.json ./
RUN npm install
COPY ./client .
CMD ["npm", "run", "start"]
# ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-service
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/add-base-url: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - http:
        paths:
          - path: /client/?(.*)
            backend:
              serviceName: client-cluster-ip-service
              servicePort: 3000
# client.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: client-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      component: client
  template:
    metadata:
      labels:
        component: client
    spec:
      containers:
        - name: client
          image: clientappcontainers.azurecr.io/client
          ports:
            - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: client-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: client
  ports:
    - port: 3000
      targetPort: 3000
# skaffold.yaml

apiVersion: skaffold/v1beta15
kind: Config
build:
  local:
    push: false
  artifacts:
    - image: clientappcontainers.azurecr.io/client
      docker:
        dockerfile: ./client/Dockerfile.dev
      sync:
        manual:
          - src: "***/*.js"
            dest: .
          - src: "***/*.html"
            dest: .
          - src: "***/*.css"
            dest: .
deploy:
  kubectl:
    manifests:
      - manifests/ingress.yaml 
      - manifests/client.yaml

So what am I doing wrong here?

EDIT:

I should note that things work fine when doing this though:

- path: /?(.*)
  backend:
    serviceName: client-cluster-ip-service
    servicePort: 3000

Repo to demo the issue:

https://github.com/eox-dev/subdir-issue

-- eox.dev
docker
kubernetes
nginx-ingress
react-router-dom
reactjs

2 Answers

12/19/2019

Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in package.json (homepage). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the URL you provide (hostname included). This may be particularly useful when using a CDN to host your application.

There are three ways to fix this problem:

  1. Add your basename to the webpack output -> publicPath(If you rejected your create-react-app project and you have already access to the webpack config file)
  2. Add PUBLIC_URL="/client" into .env.production file
  3. Add homepage property into package.json file like this:
{
  ...
  homepage: "/client"
  ...
}

Reference:

https://create-react-app.dev/docs/advanced-configuration/
-- Ali Torki
Source: StackOverflow

12/23/2019

Apparently, this is not possible currently in CRA in a dev environment:

https://github.com/facebook/create-react-app/issues/8222#issuecomment-568308139

-- eox.dev
Source: StackOverflow