Show HN: A Full-Stack Web Framework for Go (github.com)
I created a short video to show you how to create a minimal Hacker News clone with Bud: https://www.youtube.com/watch?v=LoypcRqn-xA.
The framework is free, open source and MIT Licensed. You can find it on Github: https://github.com/livebud/bud.
I started working on Bud 2 years ago after watching the introductory Laracast videos about the Laravel web framework. I was just blown away by how productive you can be in Laravel. However, like many of you, I've been so spoiled by Go. I didn't want to go back to writing PHP, so I decided to try creating Laravel for the Go ecosystem.
At this point, I just had the following goal:
• Be as productive as Laravel in a typed language like Go.
I got the first version working in 6 months and tried building a blog from it... It fell flat. You needed to scaffold all these files just to get started. If you're coming from Rails or Laravel you may shrug, this is pretty normal. Unfortunately, I've also been spoiled by the renaissance in frontend frameworks like Next.js. What I love about Next is that it starts out barebones and every file you add incrementally enhances your web application. This keeps the initial complexity under control.
With these newly discovered constraints, I started working on the next iteration. Bud should:
• Generate files only as you need them. Keep these generated files away from your application code and give developers the choice to keep them out of source control.
• Feel like using a modern JS framework. This means it should work with modern frontend frameworks like Svelte and React, support live reload and have server-side rendering for better performance and SEO.
With these new goals, the Bud you see today started to take shape. But along the way, I discovered a few more project goals:
• The framework should be extensible from Day 1. Bud is too ambitious for one person. We're going to need an ambitious community behind this framework.
• Bud should be able to provide high-level APIs for developers while compiling down to performant low-level Go code for production.
• Bud should compile to a single binary. With platforms like Fly.io and Heroku, these days it's easy to not care about this, but I still cherish the idea that I can build a single binary that contains my entire web app and secure copy it up to a tiny server that doesn't even have Go installed.
It's still super early days. You can find the the Roadmap on Github: https://github.com/livebud/bud/discussions/9. I encourage you to contribute your thoughts.
And here's the current documentation for what's already in Bud: https://denim-cub-301.notion.site/Hey-Bud-4d81622cc49942f991.... Comments are enabled for anyone to chime in.
I have big plans for the framework. I hope you'll join me on this journey to build ambitious websites faster with Go!
102 comments
[ 34.4 ms ] story [ 1048 ms ] threadi come from a ruby, js and python background using frameworks like: react, django, rails, etc. always wanted to excuse to use GO and this might be it!
Let me know when you give Bud a go! Feel free to email me at hi@livebud.com or hit me up on Twitter @mattmueller.
It also has HTMX [1] integration to create slick/modern UI behavior without writing any JS.
[0] https://github.com/mikestefanello/pagoda [1] https://htmx.org
Ent is a nice choice for the ORM. For models, I'm planning to go with a combo of sqlc + xo. I want to tie the DB schema to your application.
https://www.edgedb.com/
relevant hn thread https://news.ycombinator.com/item?id=30290225
It was a tough call, but I ultimately decided to server-render JS. This thought-experiment helped:
• If Ruby could natively render Javascript, would they still choose the Hotwire approach? I concluded no. I don't see the reason to have multiple approaches (HTML over wire, Stimulus), when one solution would suffice. The one solution also happens to be way more familiar to today's frontend developers. I definitely didn't want to make another templating language.
Bud will eventually ship with it's own JS evaluator. Either through https://github.com/dop251/goja or through porting https://github.com/bellard/quickjs to Go (~25kloc of C)
I've started using Go at work so definitely interested in using this for side-project web apps to keep me in the Go mindset.
Great questions. I answered some above, so I'll copy some and then answer your other questions below:
>> what's it doing?
Bud server-side renders Svelte files, then hydrates them on the client. This is similar to what most other JS frameworks do, it works like this:
1. Create two builds: one for server, one for browser. Both are built using ESBuild. In development, builds happen lazily upon request, similar to how a CDN like esm.sh would build JS to be imported. This will hopefully allow the build system to scale for larger apps. Vite was my inspiration here, but it's all done in Go.
2. V8 and the Svelte compiler are baked into the bud binary. When a request comes in, V8 evaluates the compiler running the server-built page and returns HTML. That's a mouthful, hopefully that makes sense.
>> what gets sent across the wire?
What gets sent over the wire is HTML. That HTML then has a script tag which requests the client-side version of the page that's used to hydrate the HTML and make the page interactive.
>> How big is the binary for the HN demo?
I just checked the binary size and it's 47.6 MB. It's mostly V8 right now, but will definitely get larger as you add more assets. I haven't tested it yet, but hopefully you'll be able to rsync just the diffs to a server.
>> What's the story for static assets like video or images
You can add them in the public/ directory. There's documentation here: https://denim-cub-301.notion.site/Hey-Bud-4d81622cc49942f991...
---
Let me know if you give Bud a go on your side-project! Happy to go into more depth here, over email hi@livebud.com or on twitter @mattmueller
https://www.whatsmydns.net/#A/livebud.com
I see views and controllers in the documentation, but the only mention of model is that "model" is a reserved directory. What are your plans for models and persistence?
>> What are your plans for models and persistence?
I haven't worked out all the details, but it's going to be some blend of https://github.com/xo/xo and https://sqlc.dev/.
Design goals:
1. High-level, type-safe "ORM" that's generated from your database schema.
2. sqlc as a fallback to cover the more complex SQL queries in a type-safe way.
The hard part is going to figuring out how to support extending the ORM with your own custom methods, hooks, computed fields etc.
What do you think?
I couldn't find anything describing the paradigm used for the JavaScript frontend. From the video, it looks like things are fully server side rendered, i.e. no client side routing as-in SvelteKit or Next.js? Are the Svelte components hydrated+interactive after page load? In my experience so far with SvelteKit and other SSR+SPA frameworks, there are too many tradeoffs with hydrating then opting to CSR for subsequent navigation. I like the browser's built-in implementation of navigating between pages because it preserves things like mutations to the document and scroll position out of the box, with no additional JavaScript. If you collapse a comment in this thread, navigate back to the home page, then use your browser's back button, the comment you collapsed should still be collapsed. In my experience with SPA CSR, you'll need to track and restore the JavaScript state driving that collapse on your own (a combination of the window.history object and CSR hooks), since navigating back to the page is a full client side re-render of the page.
I've found Svelte+React have good support for TypeScript, especially for guaranteeing the types of props passed to components, are there plans to support this? With controllers in Go and Views in Svelte/React, is there any way to help with correctness of data passed as props to the Svelte components?
I think the idea is to ingest a JS "template" and spit out the rendered HTML+JS, kind of like traditional SSR templates, but it could be possible to shoe-horn in an entire client-side router that gets initialized as a DOM object somewhere.
EDIT: I could be wrong, please correct me if so.
[1] https://github.com/rogchap/v8go
You're correct that Bud server-side renders the Svelte files, then hydrates them on the client.
This is similar to what most other JS frameworks do, it works like this:
1. Create two builds: one for server, one for browser. This is done using ESBuild. This is actually done lazily upon request, similar to how a CDN like esm.sh would build JS to be imported. This will hopefully allow the build system to scale for larger apps. Vite was my inspiration here, but it's all done in Go.
2. V8 and the Svelte compiler are baked into the bud binary. When a request comes in, V8 evaluates the compiler running the server-built page and returns HTML. That's a mouthful, hopefully that makes sense.
Happy to go into more depth here or via email hi@livebud.com.
I started my programming career after following the Rails how to make a blog in 15min youtube video. Always a good choice.
Fun fact: It took DHH a year after he first introduced Rails to get that famous weblog demo up. This stuff takes time :)
curl -sf curl https://raw.githubusercontent.com/livebud/bud/main/install.s... | sh
The last bit about single binary is quite interesting. I could easily deploy app to my raspberry Pi without worrying about bundling things. I know Go can do this but it's nice that framework aligns with Go philosophy.
curl -sf curl https://raw.githubusercontent.com/livebud/bud/main/install.s... | sh
[0] https://community.cloudflare.com/t/support-golang-natively-f...
Their reliance on V8 Isolates will make it difficult, but if they add Docker support at some point, then we're good. I don't see how Bud would work with WASM at the moment, but if someone gets it working, that would be a very pleasant surprise!
I'm more focused on getting Bud working well for deployment targets like Fly.io, Heroku, Lambda, K8s, Fargate and EC2.
_shameless plug_
I kind of made an almost similar thing can generate custom boilerplate for backend servers a while ago -> https://github.com/tompston/gomarvin
In other words, render the HTML in Go and ship the diffs to a small blob of JS running in the client over a websocket, instead of baking a whole React build into your server-side app. Much simpler toolchain this way, much easier to reason about.
I appreciate that this is a fairly different solution; one of the author’s explicit goals is that it should “Feel like using a modern JS framework”. Personally I’d rather have it “feel like I’m using Django / Rails”, except with most of the superpowers that were historically FE-framework-only.
There is no “ugh ok … better set up react now” feeling to make say an interactive chart because it was always there.
Similarly there is no “ugh ok … better set up a backend now” feeling you get using create-react-app as it was always there.
There is almost no mental gear switch as you switch from front to back to front.
The only small negative is you need to be careful what that first “server side” render produces and what it can access. But that is also part of the magic, as you can fine grain control the initial flash of content. For example include the layout/menus. Or include a cached view of a list that then updates on final render.
It just feels so well integrated more than any other framework I have tried (like aspnet mvc or RoR for example).
It will be hard to beat: If you want to beat it in Go you need at least a good Go->JS transpiler so you can use the same language. But even then anything other than JS/Typscript means I need a mental modal of what the transpiler is doing. I don’t get a free pass on understanding JS or the DOM.
I hope is that Go <=> JS/Svelte won't be too much of a context switch for folks.
I'm not planning on innovating as hard in the frontend space as Next.js. I'll probably just stick with SSR, add support for pre-rendering and build up the client-side runtime with features like prefetching and client-side routing. If something really cool comes along, we'll see.
I'll instead focus on the area where Next.js neglects: backends. DB workflows, mailers, queues, scheduling, pubsub are all coming to Bud.
Why do you think that?
As @drewry mentioned, we're aiming to be frontend-agnostic. Changing the renderer should be as simple as creating files with different extensions:
- index.svelte for Svelte
- index.jsx for React
- index.html for HTML
- index.gohtml for go/template
There's more information in this discussion: https://github.com/livebud/bud/discussions/8
Go community has been pretty "anti-framework" (AKA "just use net/http" when it clearly doesn't solve every problems..., "don't put request scoped variables in context" when it's the very purpose of that interface...) and there was a lot of drama around some framework creators, unlike any other web language community I have seen by the way (remember Martini and the insane backlash? Iris and the constant drama, was that Iris, I don't even remember the name...) so it will be pretty interesting to see the reception for that framework.
One advice to the author, congrats but don't call your framework "framework", it's like the go community hate that word, just say "a set of useful libraries to help web development", it seems like euphemisms are easier to swallow for some...
I haven't actually used generics in any serious way yet. I haven't needed them. That being said, I'm happy they're in the language now and I'm really looking forward to seeing what the Go community comes up with during this period of experimentation.
I'm aware of the "anti-framework" ethos in the Go ecosystem. I think it comes from a good place, but there's room for other approaches. Go was created to build large-scale networked applications. The Go community deserves tools that are specifically designed for building web applications. Tools that are just as productive as Laravel, Rails and Next.js.
If this puts me at odds with some Go developers, I'm okay with that. We're probably just focused on different areas. Go is a big tent with plenty of room for different perspectives.
One thing I like about the code generation approach that Bud is taking is that there's more flexibility in the API design.
Need to break out of Bud's Controller conventions? Just pass a http.ResponseWriter and *http.Request and Bud will generate the glue code to wire everything up. You can go high or low-level depending on the problem that's in front of you.
I don't think this will appease the "just use the standard library" faction, but these kind of escape hatches will at least put a dent in their argument.
Constructively speaking:
Hacker culture could benefit from less frameworks to more low code/no code platforms (which is a space with more fertile ground)
https://gobuffalo.io
Bud uses modern FE frameworks and leans heavily into making code generation work well to give you type-safety and higher-level APIs.
Buffalo is farther along in the journey with database migrations, mailers, etc.
Unfortunately, I think Buffalo is no longer in active development. The main author left his own #buffalo Slack channel in gophers.slack.com.
I personally don't find the go ecosystem very good for people who want to goto market as fast as possible, the libraries are less developed and you're going to have to build your own stack.
Bud and Buffalo seem like great developments, but your comment about the Buffalo one no longer being in active development would just totally suck if you had invested your time into building your SaaS on this.
I'm now using Ruby on Rails in the next version, and I couldn't be happier - maybe it's not as fast, but it certainly helps me ship features faster to customers!
Agreed, though when I do find a library it tends to just work. The challenge is finding the right library. There's no real reason for this though and I think with Go modules finally maturing and hopefully more web frameworks written in Go we're going to see an explosion in this space. Fingers crossed.
>> I'm now using Ruby on Rails in the next version, and I couldn't be happier - maybe it's not as fast, but it certainly helps me ship features faster to customers!
This is the spirit I like to see!
I've been using Kenny Grant's Fragmenta[1] for past 3 years and I haven't had need for anything else because I like SSR with Go and aim to keep JS as minimal as possible. I assume you're trying to address the need of those who want a Go back-end but with modern JS front-end?
I hope that you get to answer the queries in other comments regarding the design decisions reg CSR & V8 while using SSR.
[1] https://github.com/fragmenta
> I assume you're trying to address the need of those who want a Go back-end but with modern JS front-end?
Yes, that's a big reason. Half of it's preference, the other part is that most of the JS frameworks don't provide a cohesive solution for sending emails, queues, scheduling tasks, etc. The backend is a larger part of your web app than most JS frameworks give it credit for.
Their stance is to outsource the backend to third-party services, but then you're stuck piecing all these SaaS's together. I like the approach Laravel takes here: provide interfaces for Mailers, Queues, Scheduled Tasks, File Storage, etc. then provide implementations for SQS, S3, Mailgun, etc.
Just answered the question about SSR + V8 above!
Yes, Go has nice standard templating and HTTP serving libraries, and nice dependency management. These are all important features for a web framework.
No, it will never be productive as its competitors in the web framework space, because you simply cannot write expressions like `order.customer.region` that intelligently hit the database, because you can't override the dot operator. Instead you have to write unreadable crap like `regionController.Show(userController.Show(order.customer).region)` which is infinitely less "productive" than the former, for obvious reasons. Oh, you can `go generate` the *.Show definitions from a simple struct definition (or with 1.18 generics)? Too bad the final product is still unusable!
Use Go where it excels and connect it to your Ruby/Python/Typescript web backend over gRPC or HTTPS.
Writing backend with a rigid, verbose, mostly type-safe ORM: $5
Writing backend with a terse, expressive ORM: $2.50
Adding type annotations to the previous code: $1
So for authoring an app, I tend to agree with you. Languages like Ruby/Python/Typescript are probably faster to get something working.
I try to consider productivity holistically though. With Go, there's a lot less fussing with the tooling. I've also found Go saves a lot of time when onboarding new teammates. This is because of Go's intentional decision to be conservative with syntax. After all, most developer time is spent reading, not writing.
I respectfully disagree with your preference to split your backend up over gRPC or HTTPS. I prefer the approach Shopify takes to break up it's monolith: https://shopify.engineering/deconstructing-monolith-designin...
Bud will support something like this in the future.
Ultimately you will be teaching the framework, not the language, when it comes to large frameworks like this. Both Go and Python are easy to learn, but frameworks always add a lot of complexity.
> I respectfully disagree with your preference to split your backend up over gRPC or HTTPS. I prefer the approach Shopify takes to break up it's monolith:
I'm not arguing for this or that architecture cargo cult. I prefer monoliths too, but sometimes you really need distributed microservices and Go+gRPC is one of the most maintainable ways to do that.