One of my main motivations for this website was to finally create a blog. A blog can be easily set up using some prebuilt solution (like Medium for example). But I wanted to see how much fun I could have with this project by building a site from ground up. The source is this way.
I was looking for a little space to show off some general information about me - with a blog at its heart. Recently I worked with the awesome ReMirror project and wanted to utilise it for writing my blog posts anywhere on the go.
The website utilises 6 services in total:
The web build (mostly just React)
A node service to render the website statically
The API built with aiohttp
Simple nginx for static content
A custom image server for scaling and optimising uploads
And finally a mongo database
I marry all of these with docker-compose and automatic deployment using GitHub Actions. Which, I really must say, are incredible for personal projects like this and I can only recommend using them. The monthly free minutes (3000 as of writing this) are more than enough even with many, many failed builds.
Especially the node service I want to address in more detail. Getting server side rendering to work properly was actually a bit more tricky than I originally anticipated. Two particular issues I ran into were:
Duplicated "react" package in development environment - Because of the inner machinations of React you need to build and render with the same package in both environments, so if you're not "containerising" make sure to use npm link
in those instances.
Async fetches - This website relies on loading data from a MongoDB instance to display content nicely. The problem at hand is that Reacts renderToString
does not actually run any view based hooks and therefore never actually awaits fetches from effects (or runs them to begin with). I could have just gone with "best practice" and rendered static placeholders until after hydration but I wanted to go all the way. So instead I wrote a custom SSRProvider which essentially enables a 3 step process.
First round of rendering collects all the async methods that need to be run
The node server awaits all of the collected promises
Fetched data is put back into a second render cycle which then is able to display everything properly
I built the image server to deal with my uploads purely because of load time concerns, it dynamically scales each image and creates a cache. The images below show a picture from Unsplash compressed with my default settings next to one that just for the fun of it I compressed to a quality of 5 of 100.
Aside from these I added a few more fun features such as:
RSS feed - Direct link here
Simple analytics - Records user journeys without cookies, local storage or IP tracking. As of writing this I get a non-significant amount of views just from bots which is quite surprising considering there is basically no content.
Rich sharing - Just some flavour in case any link ever finds it's way out there, see twitter card example below.
Bottom line is, if it was purely about speed I should have probably just opted to use Gatsby but I also would've had a lot less fun by doing so and I definitely wouldn't have learned half as much.