1

I have a SPA (Single Page App) with JS and an index.html.

I'm serving it with my backend Golang app from /public dir.

I'm deploying this Golang app on Heroku: everything works!

Now I'm building an SSR (Server Side Rendered) app using Svelte Kit (or NextJS, I still have to decide).

GIVEN:

The SSR app is a NodeJS app which requires a dedicated Heroku dyno: I think I cannot serve it like I do now from /public dir.

Leaving aside the additional cost for the dedicated dyno, now there are also new problems of the additional latency given by:

  1. frontend SSR app (on frontend.herokuapp.com) needs to call backend.herokuapp.com;

  2. new CORS calls for the different domains

QUESTION:

Is there a way to deploy both apps on the same dyno?

Maybe with a proxy (ex: nginx or HA) in front of them so that I can have a single domain with both:

  1. myapp.heroku.com --> serving SSR app (index.html which can call my backend using /api and NOT backend.herokuapp.com/api)

  2. myapp.heroku.com/api --> which can be called from the frontend app without CORS calls

Am I totally crazy?

CONTEXT:

It's a small app with few views.

Fred Hors
  • 3,258
  • 3
  • 25
  • 71

2 Answers2

0

As per the questions and suggestions on the internet, the answer is NO.

According to many communities and experts, Heroku's model is to run one app per dyno (and often many dynos for a single app).

But wait, there is a case in which two servers are deployed to a single dyno and it worked too. You can check it here: https://medium.com/@nadayar/heroku-fu-multiple-servers-on-one-dyno-6fc68d57b373

Hope this solves you problem!

thelovekesh
  • 1,364
  • 1
  • 8
  • 22
0

Heroku allows only one port per Dyno (hence only one application is able to receive and process web requests), however there is a workaround that allows combining 2 apps in one Dyno using the Heroku Docker Registry.

The solution involves creating one Dockerfile which has both applications and that will launch both when deployed on Heroku.
Only one application (web frontend) will bind to the Heroku port and therefore accepting requests from the web. The second application (backend) will not be available outside the container while the first app is able to reach it.

Example of Dockerfile with 2 Java services (other languages/frameworks would be possible too):

RUN mkdir -p /software

# copy first app (run on $PORT)
ADD target/app1.jar /software/app1.jar
# copy second app (run always on 8888)
ADD lib/app2.jar /software/app2.jar

ADD startup.sh /software/startup.sh

USER root
RUN chmod a+x /software/startup.sh

CMD /software/startup.sh

The startup.sh must launch both applications in parallel

java -Dserver.port=$PORT $JAVA_OPTS -jar /software/app1.jar &
java -Dserver.port=8888 -jar /software/app2.jar && fg

The web requests to the Dyno (app.herokuapp.com) will resolve to the web frontend (app1 in the example above). The frontend can connect to the backend using the local address/port

http://localhost:8888/
Beppe C
  • 11,256
  • 2
  • 19
  • 41
  • Yeah. I know this. But my backend cannot get requests directly, let's say `app.heroku.com/api` hence when the frontend app has hydrated the page cannot query the backend anymore. Am I wrong? – Fred Hors Apr 21 '21 at 21:42
  • The way to call the backend would be `http://localhost:8080` (or whatever port the Goland app runs on). This is possible from the frontend is they run together in the same container. The backend will be no longer reachable from outside (public web) but this might be an advantage. – Beppe C Apr 22 '21 at 06:41
  • Yeah. I know. It's what I'm telling about. I need both SSR and ability to reach the backend from outside. This is the problem. – Fred Hors Apr 22 '21 at 13:05
  • A possible solution would be to Dockerize Nginx and both applications in a single Dockerfile but I think it is pretty complicated (installing all components and runtime starting from a base Ubuntu image) and with a good chance that the Dyno memory will not be enough. – Beppe C Apr 23 '21 at 06:52
  • Do you really need to access directly the backend from the Internet? This is normally not a good practice as you can protect it behind the other component, and proxy every request through it. – Beppe C Apr 23 '21 at 06:57
  • Why is this not good practice? Isn't that how the web works? – Fred Hors Apr 23 '21 at 10:22
  • The frontend app is responsible for handling the incoming requests and in theory it is the only component that needs to be publicly available. The backend is accessed by the frontend (to serve data or compute some business logic) and doesn't (again in theory) need to be accessed directly by the user (browser). This is the reason to have 2 layers (frontend and backend). If the browser connects directly to the backend then it makes sense to have one single app with all code and logic. – Beppe C Apr 23 '21 at 11:10
  • Sorry, I forgot to add: the good practice comes from isolating the backend component which can be invoked by the frontend only, and not from outside (no need to worry about hacking and unauthorised access to the data) increasing the overall security. When scaling up (maybe not your case) the backend can be moved to a different node which does not need to be configured for web access (simplify maintenance). Of course in our apps/projects we take shortcuts :-) and only invest time/effort if this is necessary. – Beppe C Apr 23 '21 at 11:25
  • I think we just don't understand each other. SvelteKit is like NextJS or Sapper. I need to fetch data from a backend. After the first hydration (on backend) I get the HTML and JS on my page. The next interaction is done on browser (with or without JS enabled). If I have JS enabled the call is not towards my frontend app, my app is calling the backend directly. Is it clear the problem now? Or maybe I'm wrong in how these SSR apps work. – Fred Hors Apr 23 '21 at 17:44