Why I Decided to Migrate Kaunta from Fiber to Chi
Why I Decided to Migrate Kaunta from Fiber to Chi
Recently one of my latest contributions to Kaunta is to prepare the backend to use the official Datastar Go SDK.
In case you missed the previous article, Kaunta already switched from Alpine.js to Datastar on the frontend. So naturally the next step (at least in my head) was: why not also use the official Datastar SDK on the backend? If there is a maintained SDK, it makes sense to use it instead of rolling everything by hand.
And yes, Datastar does have a Go SDK.
That’s where the plot starts.
The goal
The goal was simple.
Kaunta uses Go on the backend. Datastar has an official Go SDK. So I wanted to plug it in and move forward.
But Kaunta is currently using GoFiber, and Fiber is based on fasthttp, while the Datastar Go SDK is built on top of the standard Go library net/http.
At first I didn’t think much about it. Same language, same ecosystem, right?
Wrong.
Discussing with seuros
After realizing the mismatch between fasthttp and net/http, I had a discussion with seuros, the creator of Kaunta, on Discord.
I explained the situation: Fiber uses fasthttp, but the Datastar Go SDK is built on net/http.
He mentioned that if I could add an adapter to bridge them, that would also work.
That’s what sparked the whole adapter idea.
My first idea (and why it was fantasy)
After that discussion, I got excited.
I thought: “Alright, this is interesting. Maybe I can build something useful here. An adapter that converts fasthttp to net/http.”
In my head this sounded like a perfect open source opportunity. A bridge between Fiber and Datastar. Something others could use.
Looking back, this was mostly ignorance mixed with excitement.
Because the problem is not about types. It’s about architecture.
Yes, both are Go. Yes, both handle HTTP.
But they work in very different ways.
What I learned about net/http vs fasthttp
Here’s what I actually learned from this experiment.
- Creates new request/response objects per request.
- Is connection-oriented.
- Is built around streaming.
- Supports flushing, deadlines, connection lifecycle, hijacking, etc.
- Uses standard interfaces that almost every Go library expects.
- Reuses the same objects (object pooling).
- Avoids allocations.
- Prefers
[]byteeverywhere. - Is extremely fast and lightweight.
- Streaming exists, but it’s not the main design goal.
This is why Fiber is fast. It trades compatibility for performance.
And that’s totally fine.
But Datastar is SSE-heavy. SSE means long-lived connections, flushing data continuously, managing client lifecycle, and relying on standard HTTP semantics.
That fits net/http naturally.
It doesn’t fit fasthttp naturally.
That’s the real difference. Not speed. Not syntax. How they actually work.
”But Fiber has an adapter”
Yes. Fiber provides an adapter that lets you mount net/http handlers.
I even tested it.
Basic SSE works.
I wrote a small endpoint using the official Datastar Go SDK, wired it through Fiber’s adapter, and it streamed events correctly. I was honestly surprised. It worked.
But here’s the thing.
Adapters make things look like they work. They don’t make things actually work the same way.
You might get basic streaming today. But you’re still sitting on:
- different connection lifecycle
- different context cancellation
- different buffering model
- missing or partial support for advanced features (deadlines, hijack, etc)
So even if it works now, long term you’re just kicking the problem down the road.
This is not about Fiber being bad. Fiber is good.
It’s just not aligned with Datastar’s core model.
The SSE bug that pushed me harder toward the official SDK
Before all of this, I was working on fixing some frontend issues in Kaunta related to Datastar. While debugging those frontend issues, I discovered a problem that cost me more than three days to figure out.
Kaunta was already using a custom helper to emit Datastar-compatible SSE events. Everything looked fine at first. Signals were sent. Events arrived.
But the frontend kept rendering empty elements.
At first I thought it was a Morphdom issue (Datastar is based on Morphdom, and tables are known to be tricky). I went deep in that direction.
Turns out the bug was much simpler and much more stupid.
SSE requires every line to be prefixed with data:.
Our helper was sending:
data: <div>
Skadoosh
</div>
But what it should send is:
data: <div>
data: Skadoosh
data: </div>
Browsers only treat lines starting with data: as part of the event. Everything else is ignored.
So the frontend was only receiving <div></div> and dropping the content.
I hit this wall while fixing the Datastar frontend issues, made a simple fix for it at that moment, and opened a PR that got merged:
https://github.com/seuros/kaunta/pull/135
The PR was basically about frontend fixes, but discovering this SSE formatting issue made me realize that using an official SDK is a must. This is exactly the kind of thing an official SDK handles for you automatically. That experience became a big motivation for wanting to move to datastar-go in the first place.
Asking the Datastar community
To be sure about the best path forward, I also asked in the official Datastar Discord (really helpful and welcoming).
The creator of Datastar, Delaney, was very clear about his position. He said:
“I will never support fasthttp, never use it please”
Since he’s also the creator of the official Go SDK, that was a strong signal. He suggested that using a server aligned directly with net/http would be much nicer for this setup. He personally suggested Chi.
That confirmed what I was already feeling technically.
If the core of Datastar is built around net/http, and the creator explicitly doesn’t support fasthttp, then fighting that with adapters is not smart long term.
Why Chi
After looking into it, Chi is:
- natively built on net/http
- minimal
- boring (in a good way)
- widely used
- perfectly aligned with the Datastar Go SDK
Chi doesn’t try to be clever. It just embraces the standard library.
And in this case, boring is exactly what we want.
Chi repo: https://github.com/go-chi/chi
The real lesson
This whole experience taught me something important.
Abstraction layers have a cost.
fasthttp gives you speed. net/http gives you ecosystem compatibility.
Datastar lives in the ecosystem world.
So choosing fasthttp means swimming upstream.
This wasn’t about performance. This wasn’t about frameworks.
It was about making the backend work with the tools it actually needs.
Also, I learned that:
- adapters lie
- standards matter
- SSE framing matters
- “both are Go” means nothing
- excitement can make you chase fantasy solutions
- sometimes the boring choice is the correct one
Final decision (for now)
Kaunta does not currently use the official Datastar Go SDK.
That was the goal.
To make that goal clean and future-proof, the backend needs to be aligned with net/http. After all the experiments, discussions, and testing, I decided to migrate from Fiber to Chi. I opened an issue explaining the reasoning:
https://github.com/seuros/kaunta/issues/140
Nothing is migrated yet. This is just the direction I decided on, and I’ll be working on it step by step.
Not because Fiber is bad.
But because Datastar is built on net/http, and long term it makes more sense to meet it where it already lives.
That’s it.
Just junior engineering.