Running an SPA and an API in the same Docker Container
2021-04-28
Hosting a static web page was never a problem. A simple Apache or nginx server and a local directory on the server was always enough. With cloud environment these tools are still available. And it is simple enough to mount a directory into an nginx container. However, you still need to spin up a container just for the purpose of hosting a static page. Using a JAMstack application, you also need an API container to connect to. In order to reduce the number of containers and the overhead, I will show how we can integrate the the server for the static page into our API server. This way, we will have everything included in a single container. This is all achieved by using dockers multi-stage builds.
The Dockerfile
The container will be built using a Dockerfile which has three stages. First we build the SPA application, I have chosen nuxt. Then we compile our API server, which is built in golang. Finally we stitch together the two using a single output container based on the very basic scratch container.
The basic outline of the Dockerfile
- Stage 1: Build the SPA for production
- Stage 2: Build the server
- Stage 3: Copy the SPA and the web server into the final docker container
Obviously, you can mix and match the technology stack. Stage 1 can be any SPA oder static site generator you want. For example Angular, Gridsome, or React. The same hold true for the API part. Use whatever architecture you want. You just have to make sure that it servers the SPA as a fallback and for the root of the web address.
|
|
Remarks for the SPA build stage
The beauty of this approach: no changes are required for the SPA.
The only thing that comes to mind is not really a requirement but it reliefes some stress from the development part of your application: establish a small dev-proxy to forward SPA http requests meant for the api to your local development intance of the API server.
In nuxt, this is done using the @nuxt/router package:
|
|
All SPA frameworks like [Angular]https://angular.io/) or React have the option to implement a small routing roule to ease the development locally. For production routing will be covered by our API server actually providiing our endpoints.
Points to look out for in the API server
I think this is the most important, but also most obvious part of the changes required: the api server needs to serve the SPA/static html as its root. For Golang this is achieved with a single SPA handler registered at the root level of a mux router:
|
|
The code shown above tells our api server to serve the static page base file “index.html” from the indicated folder. This is the same folder we copied the genreated SPA files in from our stage 1 build in the Dockerfile