facebook/create-react-app

request for "serve app under" configuration

ggerrietts opened this issue · 22 comments

The Issue

Currently, no matter what you do with routes and such inside your app, the Webpack node app that does all the hot module reloading and linting and such always wants to serve assets rooted at /. This breaks when a development environment includes more than one interoperating React app sharing a URI path space.

The Proposed Solution

We would really like to be able to serve those assets from a configurable path. It would be nice to be able to specify in our package.json some setting that identified a "mount under" pathspec. I think we can be somewhat flexible about the exact behavior, so long as the asset links Webpack generates contain a configurable path prefix.

The Use Case

We're using create-react-app to build two separate React applications. One application is our domain application. One application is a common user-and-organization management app. The user-and-organization management app is shared with other business units who are using completely separate stacks.

We're still relatively early in the development process, and to date, we've been fine using different ports to route between these apps. However, a new set of requirements requires us to gather the two apps under a single domain name. The user-and-org app should be mounted under domain.name.here/account and the remainder of domain.name.here should serve the domain-specific app.

In production, this shouldn't be a problem. We can emit a bundle that will have the right paths in it by using the homepage setting. We'll stick both bundles onto a CDN; a load balancer will route the API endpoints, and everything should be fine.

In development, things are a little trickier. Webpack's development app always wants to serve assets from /, and it writes all the asset links it emits to reference that path. We can handle getting requests with /account in them to the correct server, and we can write our user-and-org application to expect /account in front of all the routes, but we can't talk Webpack into serving assets under /account -- we could work around this problem -- nor can we get Webpack to emit links to those assets under /account -- and that problem we can't work around.

Workarounds

We've talked about continuing to route by port in development, and just making all our link-generation logic environment sensitive.

We've talked about ejecting and writing our own Webpack config -- we actually started down that path, but I don't think we've reached its conclusion.

We've also pushed back a little harder on using domain names to separate the two apps; that has some upside and some downside, but it obviates this problem.

If homepage ends with /account, would it be reasonable to serve under /account in development?

For our use case, absolutely. For us, it's even preferable that nothing cares about the protocol/domain/port parts of homepage.

how about basePath config in package json or BASE_PATH env?

Timer commented

@viankakrisna we like to avoid adding additional configuration as much as possible, and homepage in package.json already serves this purpose. 😄 Did you have a case that you need help with?

Timer commented

@ggerrietts: I believe @gaearon and I would both be open to a PR that enables this functionality so that we can play with it.
We need to make sure we understand the implications of the change fully and make sure we don't break any of webpack's HMR or other functionality.

We would greatly appreciate the contribution. 😃

@Timer yeah, I think homepage would be sufficient. Doesn't really found any use case that setting it to "." or PUBLIC_URL env would not solve.

For @ggerrietts use case:
maybe you can setup an nginx server that map your.app.dev to localhost:3000 (your main app) and your.app.dev/account tolocalhost:3001 (your account app). Haven't really tested if HMR works with proxying though

Timer commented

@ggerrietts are you willing to open a PR providing this functionality?

Timer commented

Tagging for 0.10.0 since this would be a breaking change and we want to make sure it doesn't slip through the cracks if it's ready then.

Hey, I have this issue open in a tab in the hopes that I'll get bandwidth to work on it. I think my employer has opted to go with the final workaround listed above -- use domain names instead of mounting sub-apps -- so I need to fit it in after-hours. I have some things to learn, but my hope is that maybe I can deliver such a thing over the weekend. I have four kids, though, so sometimes I don't get what I want. :)

We are also having this problem. Our situation is that we have a monolithic Rails app which we are beginning to carve out into smaller React apps. For this reason, a different domain does not work. @Timer and @viankakrisna please be aware that cookies won't be sent when the ports are different. This breaks our app too (don't ask :P)

This is our first time using create-react-app. In our other React apps, we've had to configure basePath in Webpack, and are successfully using this in production behind Akamai.

This is up for grabs—please feel free to contribute the implementation.
My current thinking is it should use the same public path as in production.

For the cookie thing you need to have same top level domain. So the workaround is setup nginx server locally that maps the pathname to the devserver with a same top level domain. (haven't tested it, but that's the best solution i can think of)

The simpler way is to have cra generate development bundle and include it in your app.
There's a pr open for that in #1616. But this feature needs more exploration and validation, cause right now we lose access to hot reloading and error overlay. Also there's an added risk of deploying development bundle to production.

Currently, I think cra is more suitable to build a statically served single page app with an api server rather than integrated with monolith app with access to cookie etc for development.

Should this work happen off of master? Or the 0.10 release?

I see there is not a 0.10 release branch sorry my bad.

I have some of this working locally now. I am not sure all the cases to test though.

in testing, I created a new issue:
#1880

couldn't see in the issues well enough if this was reported or not, but I found similar issues reported as well on just webpack dev server.

opened a pr for this.

Big thanks to @kellyrmilligan for starting work on this in #1887. Unfortunately I could only review it now, and I found a few blocking issues with it.

@kellyrmilligan Please let me know if you’d like to address those further in #1887, or if you’d rather have somebody else finish up the pull request. Since we plan to cut 0.10 very soon, I’m afraid I have to postpone this feature until 0.11, as it is not currently quite ready to be merged. I realize it is frustrating.

I will see what I can do in the next week or so. I really need this feature! Totally understand to postpone until 0.11

Yea, we want it too 😛
I’m sorry I couldn’t review earlier.

While you are already about to solve this problem, I thought I would offer a small step-by-step workaround for those folks who are working with addressing static resources from their in-project CSS files. Feel free to ignore this in the issue - but it may help someone out there, so adding it as a comment. Fair?

The situation can be worked around (somewhat clunkily, but still...) with a rewrite rule in the web server serving the production build. Example below, and a small recipe for the rewrite rule required as well.

Description

Given a CSS file ("global.css") which links to a font placed within the static folder public/fonts:

[project] / src / styles / global.css
[project] / public / fonts / Fondamento-Regular.ttf

In order to work in development mode, the entry in global.css must contain the entry url(/fonts/Fondamento-Regular.ttf):

@font-face {
font-family: 'Fondamento';
font-style: normal;
font-weight: 400;
src: local('Fondamento Regular'), local('Fondamento-Regular'), url(/fonts/Fondamento-Regular.ttf) format('truetype');

Assuming that the context path of the production build should be /mithlond, the package.json entry should be "homepage" : "https://[someServerName]/mithlond",.

However, running the npm run build to generate a production build, the links inscribed within the static/css/main.[hash].css do not contain the context path, but are copied verbatim:

url(/fonts/Fondamento-Regular.ttf)

(Somewhat clunky) Workaround recipe

  1. Ensure that your static resources do not reside within directories "public/js" or "public/css". I chose "public/font" for the fonts in the case per above.
  2. Create a text substitution ("rewrite") rule within the server, on the context path of the application. For nginx, the rule is as follows (and repeat the sub_filter bit for all directories of static paths you include in your application:
    location /mithlond/ {
        # Prepend the /mithlond/ path to outbound font links
        sub_filter "/fonts/" "/mithlond/fonts/";
        sub_filter_once off;
        sub_filter_types *;
    }

The sub_filter above effectively replaces the malformed URLs embedded in the compiled CSS resource so that the outbound links work for clients.

Timer commented

We definitely want to go forward with this. Please consolidate all discussions into #1887. Thanks!