4

I'm writing a react SPA that will be hosted by various clients in their own K8S clusters which I bundle as an image file. I have no idea up front what the hostname might be, nor what subpath they might choose to expose the app on (I'm assuming they'll use ingress to expose the app on a given subpath (HTTP routing) rather than using a port, but they might use both approaches.

The problem appears to be that the app requests resources (JS, CSS, etc.) from an absolute path (i.e. one that starts with a /), rather than relative to the index file, therefore failing with a 404.

I've done some research, and it's possible to set a homepage entry in the package.json file, but that's only used when building and I don't have the required info when building, besides, this requires the full host, port, and subpath, which should be even less necessary than simply the subpath, and it's only available if one uses create-react-app. Another suggestion is to use the process.env.PUBLIC_URL variable, but again, this is only available during building and that information isn't available.

Finally, I've also tried to configure the react router to deal with this problem, even though it needs to know the route during the build:

    <Router basename={'/tui'}>
      <Route path='/' component={Home} />
    </Router>

But that, obviously, doesn't (and couldn't) work either. The main problem is js imports again.

Another suggested answer (below) is to use a script to rewrite URLs at application startup. This won't work for imports. It assumes that my app is all rolled up into a single file. This isn't the case with this app, where for some very good reasons, we can't roll it up into a single js file.

To be clear, my js imports are as relative as possible. So:

import ReactDOM from "react-dom/client";
import Page from "./Page.jsx";

The Page.jsx file is defined relative to the current file (I have a flat src directory), and the bundled libraries are imported as object references, not file references, under the assumption that the file has already been fetched in some way.

So, what to do? Is there a known way of solving this problem that allows me to run my react app on a subpath without specifying the subpath (or host or port) during the build?

Software Engineer
  • 15,457
  • 7
  • 74
  • 102
  • Have a look at [this other answer](https://stackoverflow.com/questions/75026414/how-to-deploy-a-build-react-app-in-your-existing-laravel-site-as-subdomain/75113560#75113560) of mine. It involves using the HTML base tag and feeding its value into React, then setting a base path for the react router. – Gary Archer Jan 13 '23 at 19:32
  • Check the Github example I’ve provided below. – Flavius Condurache Jan 22 '23 at 09:57

2 Answers2

0

the one I've used before with URLs compiled into JS was using replacements with envs in startup scripts of the container like :

#!/bin/bash
sed -i "s|some.default.baseurl|${BASEURL}|g" /some/path/main.js
/entrypoint.sh

notice the use of | as separator in sed instead of regular convention of using / as this one can be used in path part of baseurl.

Radek 'Goblin' Pieczonka
  • 21,554
  • 7
  • 52
  • 48
  • That doesn't work for js imports though, which aren't loading a URL (ultimately, the browser will issue a fetch for the imported file, but it's not represented as a URL in the code). – Software Engineer Jan 04 '23 at 11:15
-1

It's tricky to do, but it's doable. Basically the replacement of a placeholder with an environment variable is how you do it, but you have to expand it a bit.

You can check here for an example.


Original answer:

They could use path rewriting to fix the issue. You serve your app normally on / and if they wanna serve it for example on example.com/myapp/ they can use path target rewriting to send the request to your container on path /

Here is a code snippet:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: "rewrite"
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
    - http: example.com
        paths: 
          - path: /myapp(/|$)(.*)
            backend:
              serviceName: your_app_service
              servicePort: 80

In the above ingress definition, any characters captured by (.*) will be assigned to the placeholder $2, which is then used as a parameter in the rewrite-target annotation.

This ingress will do the following:

  • Requests to /myapp will be delivered to your_app_service as /
  • Requests to /myapp/contact will be delivered to your_app_service as /contact
  • Requests to /myapp/css/main.css will be delivered to your_app_service as /css/main.css

Also note that postgres ingress uses path priority.

karel
  • 5,489
  • 46
  • 45
  • 50
  • My example uses nginx ingress but if you wanna use ALB for AWS then you need to add a proxy between the ingress and the service. I use nginx. and in the proxy you can do the path rewrite. Also this should if you change the host or domain or port. – Flavius Condurache Jan 13 '23 at 12:43
  • That is what causes the problem, not what solves it. It is by trying to run the app through kubernetes ingress that the problem first appeared. And, of course, my ingress controller is functionally identical to this one. The problem isn't serving the app on a subpath, it's the fact that when you do, the app requests its assets without using the subpath. – Software Engineer Jan 13 '23 at 22:22
  • Ok, I got it backwards then. I'll rewrite it thanks for clarification. – Flavius Condurache Jan 13 '23 at 22:32