I Rewrote My Website

#golang #coding #webdev

My website was written in SvelteKit, a framework i still very much like. But i started on a new team at my job that is more backend focused and writes primarily in golang, so i wanted to get some practice by rewriting something familiar to me.

This was also an opportunity to rip tailwind out and go back to vanilla css which i have learned i much prefer especially since nesting became native.

New image architecture

flowchart TD A["request /blog/{slug}"] --> postFound{post found in blog.Posts?} -- yes -->B["filter blog posts for {slug}"] postFound-- no --> getPosts["call getPosts()"] getPosts --> B B--> C["pass post for {slug} into goldmark for rendering"] --> D["goldmark custom extention finds images and gets a signed URL for that image from the bucket and minio instance given in the extention config"] --> E["Signed URL uri is set as the src for an img tag i.e src='s3/{signedURL uri}'"]

(if the mermaid diagram doesn't render, you may need to refresh the page. It won't render without JS enabled, sorry! I tried SSRing it but it was taking >1s and i didn't dig into the alternative SSR method. Maybe another time...) If you see my last post, you will see that my blog has images now. The funny thing about that post is that it is now mostly out of date 😅. The good news is that part of it is similar. Instead of SvelteMarkdown, I'm using goldmark and a custom extension to fetch a signed URL from my minio instance and i set an img tag with the src of the URI of the signed URL, then i set up a route on the server at /s3/ to reverse proxy to my minio instance so that i do not have to expose it publicly.

Open Telemetry

I was using pino-loki previously for logs but i wanted to delve deeper into understanding observability stuff since its still kind of new to me. This is the first time i've instrumented an application from scratch and after wrapping my head around the concepts it wasn't too bad. Although there is certainly some things i'm missing. It was hard to find definitive best practices as most guides i found were related to large distributed systems. Which makes sense. I did get really stuck on the zerolog otel bridge which i thought had a bug for a long time until i realized the bug was in zerolog itself. It can not transform fields into otel attributes, and the otel-zerolog bridge was actually unpublished. But the documentation hasn't been updated to reflect this so i was befuddled when trying to understand why the attributes weren't showing up in Grafana.

I switched to the otel slog package which is fine but doesn't seem to output in json format. The message itself doesn't seem to have a field attached so the | json formatter in Grafana doesn't work. This is not a huge deal but there are some l things that seem difficult to config compared to zerolog.

Styles

I am using Open Props which i love because it provides great defaults for sizes and colors and other various things. It makes writing a stylesheet from scratch a lot less daunting. I learned about the light-dark() css function which i made use of for my themes. Unfortunately the light-dark function doesn't work with gradients so i had to ditch the gradient on the "hashtags". One day those will be clickable and will sort blog posts by the clicked tag but that feature will have to wait.

Other stuff

I am planning on writing a colophon page to list all of the tech behind this blog but for the rewrite I used the following libraries:

  • golang 1.24
  • templ for templating
  • htmx (only using the boost feature currently)
  • open props
  • goldmark for markdown rendering
  • otel, otelslog
  • minio
  • net/http for routing and http handling

The blog is deployed to my k8s cluster. Right now the only CI i have builds a staging image on the staging branch and the production image on the main branch. I would like to get the k8s definitions saddled up with the code itself but i have a messy homelab repo full of yaml, helm commands in text files, and some deno script that lets me write typed k8s definitions that spits out yaml. Eventually that will be made into something more reusable and i can define those definitions in the same repo as the code i'm trying to deploy. It's good to have goals 😄.

Golang thoughts

There are a lot of things i really like about golang, and some things that are not great. First, the bad: documentation is very obtuse. I will admit this is partly a skill issue on my part, but there way things are abstracted means the generated documentation for libraries is often extremely confusing. I am used to digging through TypeScript definitions to better understand an API but for golang even that is sometimes difficult the way interfaces are used abstract things. This is something that will get better over time as i become more comfortable with the language but it makes it difficult to learn.

The things i like are mostly what i see others who are new to golang like: the error handling, the "one way of doing things" style, the toolchain, and the standard library. That last one is probably because my main serverside language is TypeScript and outside of something like Deno there is no standard library.

Obsidian sync

My blog engine also got a little update with this rewrite. There was a bug that because i relied on the file's ctime and mtime, when syncing a vault to a linux machine, the ctime would be incorrect because the filesystem doesn't track a createdAt time, only modifiedAt. The solution is to store this time in the frontmatter which is fine but in order to fix files that already existed, i had to write some code that injected the frontmatter when fetching files that did not have this new format. New files also get this injected and an on modify hook lets the mtime update automagically. This let me also change the timestamp to use RFC1123 so I can use the mtime as my Last-Modified header for better caching semantics.

That is all for today. Thanks as always for reading and i will catch you on the internet.

Created on:
Last updated:
<< Go Back