I just realized the other day that my blog is in its fifth year now. Since the application is built from scratch, I figured it would be fun to do a write-up of the architecture behind my blog.
Build
My blog has always been a NodeJS/Express application, but lately I have made some changes to how it’s built and deployed. The first thing I changed was the underlying build system. Switching the build to Bazel made for a much more optimal build pipeline. Instead of a cobbled together Typescript build with custom Azure deployment scripts, I now do everything in Bazel. This allows me to set up a completely consolidated build pipeline.
Most of the code is written in Typescript, so the first steps is transpiling Typescript, but Bazel also builds my comment section that is created in a Javascript framework called Svelte. You can read more about my Svelte comment section here.
Deployment
Handling compilation of source code written in different environments is one of the strengths of Bazel, but compilation is usually not the leaf of the build process. The next step is typically to package up the build artifacts and deploy the application somewhere. In my initial solution I did a Git deploy to Azure, aided by a custom shell script. This approach worked reasonably well for years, but the script was kind of a mess. Every time I needed to make a change, I was reminded how brittle the approach really was. It ended up becoming a pain, so I decided to replace the custom build script with a Docker based Bazel solution. Now, as part of every release, I use Bazel to create a new Docker image that I push to a private Docker registry. I have a web container app running in Azure that I re-deploy based on the most recent Docker image. Before, my deployments used to be very slow since the build always started from scratch, but with Bazel my builds usually only take about 30 seconds since the entire build pipeline is incremental.
Database
My articles and comments are stored in a MongoDB instance, but I rarely hit the database directly since all articles and comments are cached in the Node layer. This means I can get by with a very low end MongoDB instance, which helps keep hosting costs down.
CDN
Based on Google Analytics I see visitors from all over the world. To help with network latency I have configured Cloudflare as a proxy in front of my Azure hosted application. This functions much like a CDN where visitors will be served static assets like css and Javascript from a location closer to their own location. Cloudflare offers a free tier, and is relatively easy to configure. The only thing you may have to look up is how to change the dns name servers with your domain provider.
Static Html
Under the hood I use NodeJS with Mustache templates to generate articles dynamically, but there is not reason to regenerate the articles for every request. Instead I run a build time batch process to generate static html pages for all articles. This means my NodeJS server only needs to serve static html documents at runtime. On top of that I also cache the static html documents in the CDN. This helps reduce the resources needed by my server since there is no heavy code execution. More about this process in my other article about static html generation.