Hi, folks! I’ve just launched an Apollo Server/Client site which will get a fair bit of traffic, on a Google Cloud VM, and I haven’t been able to find any discussion of optimal ways to do production hosting of Apollo Server/Client. What I’m doing right now is questionable: simply running the server (using npm start with NODE_ENV=production) and the client (with npm run build and serve -s build) in screens. Yes, screens.
I could certainly put these into systemd scripts, but does anyone have recommendations? I’m a bit surprised to find so little on production hosting of Apollo Server and Client to be honest. But maybe I’m missing something.
There are steps you can take.
First - you can use something like “forever” to make sure that if your app crash or server goes down or something else happens - the app will be restarted.
Now on the client-side you really don’t need “serve”. The client is a simple static website with HTML and JavaScript. Any hosting that can store static files and serve them cheaply will do. And I suggest placing the front-end on a separate machine. In my case, I have projects on both Nginx and Lighttpd. Just build your frontend and then whatever directory contains index.html with all javascript files and stuff (build dir) - set document root of your server to that directory - that’s all. If you need HTTPS, the easiest way is to use this:
I will explain why you want the front end separated in a moment.
The server is a different kind of beast and usually is heavier. Also, the downside of using graphql is that developers tend to request a lot of data at once and that can cause strain on the server. And your app will start causing troubles once the CPU (probably) will start throttling. So usually if you expect traffic during some events or something it’s smart to make sure it will scale. But generally, the server apps unlike the frontend will have to run using node.
Now, why would you need a separated frontend and backend? I have a chat app that uses WebSockets. On a typical day, it’s used by 20-30 people but there are times when we have like 600-800. And they are very active. On that days I run a few instances of the backend with a load balancer in front. And I have small auto-scaling logic when my app uses around 60% of the CPU core (that’s my bottleneck - I will run out of CPU before anything else). This ensures that my app always works perfectly. It will run more instances on bad days and go back to 2 instances (always have redundancy) on good days.
Now reason, why I separated the frontend from the backend, is simple. I have 0 reasons to have more than 1 instance of frontend because those are just static files and even a couple of thousand people do not create any issues for me. So I always have just 1 instance of frontend running on very light HTTP server.
Now if you have knowledge or someone with that knowledge there is another way to do it. Especially if you are already using Google Cloud. Use Kubernetes. Dockerize your app. Basically create images with them. And deploy them on Kubernetes. This gives you a few things out of the box.
- You can build your app and store it in a private image repository. That image can be used to run tests. And then it can be taken and deployed on production. And it will be exact same image. This makes sure you have minimum differences that can cause problems for you. Also if there are any issues you can deploy the same app on minikube on your local machine and test it directly.
- If you need to roll back to the previous version of the app or something or check some old issues you can download old images (if you keep them) and run the old version of the app. All dependencies and build code will be already inside the image so no “npm install” will be needed. You will have exact same image you had a month ago.
- Kubernetes allow you to set up autoscaling rules to do what I described previously. Run more backend instances if you will have extra traffic.
- Deployment with Kubernetes is super easy. Kubernetes will spin instances of the new apps along with your old app, redirect traffic to the new app and that’s it. It’s a little bit more complicated when you have database migrations etc. But generally speaking, it’s basically 0 downtime deployment in most basic situations.
I use docker and Kubernetes for all my apps.
Great stuff, Darius! I have no idea why I wasn’t hosting the client via Apache, that was just silly of me. And I’m using forever on the server now. And thanks for the points about separating the two, I’ll see how things go on the single server (which is also hosting Postgres with the data and Tomcat providing the REST API). Seems to be pretty good and traffic is heaviest this time of year. Much appreciated! No more screens!
Oh, hrm, maybe I spoke a bit too soon. When I simply run Apache with DocumentRoot .../build
the main page works fine. But I’m using BrowserRouter to handle some other paths, and those break when I’m hosting it statically from Apache. (Page not found, which makes sense). But if I use serve
and proxy to localhost:3000 from Apache, those Route URLs work fine. Thoughts? I thought serve
was just a quick and dirty http server but I guess it’s handling those paths in some special way.
OK, silly me again. I was using serve -s
which rewrites not-found URLs to index.html. And the Router sees the URI and does the right thing. So the Apache equivalent (perhaps not totally kosher) that I implemented is ErrorDocument 404 /index.html
. Routing now works.
It depends on server configuration.
Usually you want all your paths to be directed to index.html so JS can take care of everything.
If you get errors, that usually means that path is first interpreted by your HTTP server and then because file under that path does not exist - you get 404.
1 Like