top | item 34246960

Pushup: a new compiler for making web apps in Go

215 points| todsacerdoti | 3 years ago |github.com | reply

119 comments

order
[+] paulsmith|3 years ago|reply
Pushup creator here. We're interested in feedback on the design. Pushup introduces a new lightweight template syntax that mixes Go code and HTML in the same page; pages are then compiled down to pure Go, relying on the net/http package. Pushup uses file-based routing, so adding a page adds a route to your app. The template language has an "inline partials" feature to make it easier support for modern hypermedia libraries like htmx in mind. Would love to know what the Go web development community thinks.

Here is a short demo of Pushup in action: https://www.youtube.com/watch?v=nkyiATkZ4Js

[+] jerf|3 years ago|reply
"Pushup uses file-based routing, so adding a page adds a route to your app."

Make sure to have a clean way of escaping that and having some richer concept of how to route. This was the original way routing worked, back in the ASP (not ASP.net, the predecessor), CGI, original PHP days. We moved away from it for good reasons. It works well early but has scaling problems over time.

I'm not saying don't have it as a default per se... just make sure you have a "if you need to do something else here's how to override it".

I also strongly advise against trying to guess what the overrides might be and building special cases for "the three things I think people may need to do beyond thsi". I've got a router in my system that looks at client-side TLS certificates if it comes from certain IPs and uses LDAP authentication if it comes from certain other IPs, and does different things if the auth fails for each case. You'll never be able to guess all the possible ways people want to route. You need to make sure you don't lock the users into this so hard they can't escape except by leaving your framework entirely.

Edit: Also, I didn't look into this deeply, but if you throw away the ability to have middleware on specific routes you've really caused a big problem. The Go ecosystem is fairly dependent on that.

[+] savolai|3 years ago|reply
I’m Finnish. The caret is not always easy to type in my experience on international keyboards. Not sure what the added value is of creating yet another convention here, as there are so many already with each new templating language.
[+] oppositelock|3 years ago|reply
This is very neat, but you are delving into a very complex world, as you are well aware. In your video, you have generated static server side pages, without any JS, where your annotated HTML uses the embedded go to generate static HTML.

This is much nicer syntactically than using the Go html/template engine, but it seems roughly equivalent in expressive power. Are you converting your "up" syntax into go templates with the Go expressions extracted into compiled Go code, referenced be the templates, out of curiosity? If so, the way you've transparently handled interleaving (such as html elements in the for loop) is really cool.

How would your go scripting interact with JS? For example, say that I have a backend API that the fronted calls into. In your design, would I call out into Go to perform the http request, or would I do this in JS? I'm sure both would work - since the Go request would be handled server side and simply slow down static page generation, but it seems like calling into Go might not be the right thing to do for a more responsive AJAX app. Do you envision mixing Up/JS in the same pages? Can I do crazy stuff like use JS to insert a variable (by value probably) into your Go code, or vice versa?

Over the years, I've learned that web front ends are positively bonkers in the kinds of things they want to do, and when you are the developer of any kind of frameworks, you end up suffering greatly if you insert yourself into middle of that ecosystem, since you will be asked to support features that you never dreamed of. If you support them, the framework gets more use, if you don't, the cool kids move onto other things.

I've tried to tackle a much simpler problem with a project of my own [1], which is a backend server code generator to make implementing JSON models more easily from an OpenAPI spec, and I've found that even in this relatively simple concept, the limitations of a strictly, statically typed language like Go end up running into incredible complexity due to the dynamic nature of web frontends and JSON. Outside of trivial strings and numbers, you start running into the limits of the Go typing system.

Anyhow, good luck, this is very cool and it seems like a fun thing to play with.

1: https://github.com/deepmap/oapi-codegen

[+] digitalsankhara|3 years ago|reply
This looks really good. I like the compilation to a single binary. I'm already using Htmx with Flask for routing/templating. File based routing with a page as the unit of functionality sit well with me.

The use of caret to denote Go code is quite clean, but given well formed HTML, could the parser detect anything not inside HTML tags as Go code and thus remove the need for a caret outside of HTML.

I will be trying this out. Thanks for this project.

[+] mholt|3 years ago|reply
Hey cool!

Caddy plugin? :D I might be quite interested in this.

[+] iamgopal|3 years ago|reply
Also try to give usable web app ( todo ? ) hostel somewhere if possible.
[+] KRAKRISMOTT|3 years ago|reply
Do you have any hosted demos?
[+] jakelazaroff|3 years ago|reply
This looks really promising — at first blush, I like this a lot more than normal Go templating! That said: maybe it's just my bias as someone who primarily works with React, but I'm wondering why not just mimic JSX, which has been incredibly successful and has millions of people who already understand it.

The big missing piece to me here is composability. How can I encapsulate reusable bits of markup, styles and presentational logic? In most JS frameworks, I'd extract them into a "component" (in React, that just means a function that returns JSX). Pushup has "partials," but it's not clear to me whether they're reusable or if that word means something different in Pushup than it does in other templating systems. I see that layouts can denote sections to be filled in by pages, but IMO that's backwards — a page now needs to know details about its "call site".

Using ^ as a delimiter seems odd, but I suppose that's just a familiarity thing. I do wonder though how you handle your example of

    <p>The time is now ^time.Now().String().</p>
without having to backtrack, since that last period is ambiguous in this grammar.
[+] paulsmith|3 years ago|reply
> why not just mimic JSX

Definitely considered that. Might be worth revisiting.

> The big missing piece to me here is composability

You zeroed in on the right things ;^) I have a draft note to myself about pages-as-components. I have some ideas (basically, mimic ASP.net Razor components at the moment). Having a compiler with full control of the page makes this easier to figure out.

> without having to backtrack, since that last period is ambiguous in this grammar.

It doesn't backtrack, it just looksahead at the next token: https://github.com/adhocteam/pushup/blob/main/main.go#L3692

[+] HALtheWise|3 years ago|reply
I actually hacked together "JSX in go" over a few weeks a couple years ago, targeting wasm/GopherJS so the code could actually execute in the frontend. It was a really fun exercise in reading/understanding the internals of the go compiler.

https://github.com/8byt/gox

[+] andreygrehov|3 years ago|reply
When it comes to traditional web frameworks, the most important part is handling regular HTML forms. Reason being, a form is the only native mechanism your customers have to use to interact with your service. Forms are important. Specifically, validation and error handling. I think Laravel does it really well. It's been a while, but Laravel has a wrapper around HTML forms. This wrapper does everything you need: server-side form validation, form generation, flexible error handling that takes care of client error messages, placeholders, input values, css classes for invalid states, you name it.

Does Pushup help engineers with HTML forms?

[+] paulsmith|3 years ago|reply
Not yet, but that is the intent. As a long-time Django user I have a high bar for the kind of forms support a framework should provide. One of my thoughts/beliefs is that, as a compiler with complete control over the page, there is a lot Pushup can do on both the markup generation side as well as checks and validations. eg., automatic CSRF token insertion, compiler "warnings" for accessibility, etc.
[+] chx|3 years ago|reply
Regular HTML forms are hard. Source: I was the Drupal form API maintainer for a long time. Way too long.
[+] miohtama|3 years ago|reply
This plus CRUD pattern - after the submission you want to edit the data usually with a slightly different form.
[+] randomguy0|3 years ago|reply
Any more info on this form wrapper for Laravel?
[+] dakiol|3 years ago|reply
As others have commented, just go for <?go ... ?> instead of the caret. You'll get thousands of users in an instant.
[+] denysvitali|3 years ago|reply
So basically PHP, but in Go? I love Go, but I honestly hope this doesn't become as widespread as PHP. There is a reason why we switched away from template-based frontends (à la PHP).

On the other side, it's also true that some frontend frameworks are partially moving back to SSR...

[+] kevinfiol|3 years ago|reply
> There is a reason why we switched away from template-based frontends (à la PHP).

Can you elaborate on that reason? Genuine question. As you mentioned in your post, a lot of what newer web frameworks are doing (SvelteKit, Astro, Enhance.dev, etc.) are reminding me a lot of my early days with PHP. The benefits of SSR are widely acknowledged amongst the proper JS frameworks.

[+] dakiol|3 years ago|reply
> There is a reason why we switched away from template-based frontends (à la PHP)

Who is "we"? There are more websites out there written using template-based frontends (mostly PHP) than the other way around.

[+] paulsmith|3 years ago|reply
> So basically PHP, but in Go?

You say that like it's a bad thing ;^)

[+] shadowgovt|3 years ago|reply
One counterpoint is React is doing gangbusters right now and uses the JSX syntax extension to embed HTML directly into JavaScript or TypeScript.

To my money, the main issues with PHP had little to do with template embedding and everything to do with Perl being a messy language for writing correct code (coupled with too many people in the era underestimating the importance of "correctness" for HTML rendering when the ability for a malicious operator to mutate HTML on a page can result in all manner of security exploits). Perl just has too many ways to write accepted almost-correct code that breaks abstraction / fails to string-escape in subtle and whole-farm-losing ways.

[+] techn00|3 years ago|reply
Copying React's JSX would be sooo cool (like Rust's https://dioxuslabs.com/ ), or something like phoenix liveview for elixir.

I don't like the "^"

[+] teejays|3 years ago|reply
It's a nice idea. I'm going to dig a little deeper in the design and developer experience choices.

That being said, I am a little curious -- what is the intended use case here? The reason I am asking is because I've been doing lots of web-development, with Go as a primary backend lang, for the last 9 years but never have I seriously felt the need to do serve the UI from Go. I guess there is some advantage reducing n (number of tools/languages in a stack) - but is there any other motivation behind it?

[+] TobyTheDog123|3 years ago|reply
This looks great for simple projects, but I'm curious who is working on projects small enough to not be worth a separate dedicated and feature-filled frontend (ala JS/Flutter), but large enough to warrant server-side logic.

That's certainly not to say they don't exist, and Pushup is certainly filling a niche, but my mind immediately goes to those "forgotten" services like email unsubscription and confirmation links.

[+] shadowgovt|3 years ago|reply
This looks neat.

Always be wary of this on a new web framework:

> Pushup is an experiment. In terms of the development life cycle, it should be considered preview pre-release software

The biggest risk with such software as a web framework is security exploits. The nuances of the HTML escaping dance are subtle and hard to get right.

(Not to say this gets them wrong or can't get them right, only that all such code should be assumed-risky for security until proven otherwise).

[+] nprateem|3 years ago|reply
TBH all I want are Django's generic views implemented in Go to make it easy to map routes to DB resources to speed up making APIs.

Ideally it would automagically expose a whitelisted list of DB tables as API endpoints but allow the ability to override queries for each table and perform logic as necessary (i.e. support custom handlers as well as lightweight default ones).

If anyone knows of anything like that please let me know.

[+] TobyTheDog123|3 years ago|reply
There's an interesting conversation to be had about developer ergonomics. For me, on a Mac, it's far more easier to use characters and symbols on the bottom of the keyboard.

<script></script>.

It's even easy to use a select few symbols from the number row.

!, @, #, $ uses my left pinky on the left shift, left hand middle (or index) finger on the number

&, *, (, and ) use my left pinky on the left shift, right hand finger on the number.

^ (and % + & to an extent) seem to be a no-mans land, similar to smart phone screens only allowing limited easy reach.

If you're going to be typing a symbol character to mark a variable (or whatever else) it certainly needs to be easy to reach, and for me at least, the ^ isn't.

Maybe it's just my typing style or that I have small fingers :(

Edit: Also, what about IDE plugins? I would hate to lose type safety or smart suggestions for a line like this:

<p>The time is now ^time.Now().String().</p>

[+] kroltan|3 years ago|reply
Also, some keyboard layouts use dead keys for inputting ^ as a diacritic (for example, ABNT2 Portuguese, the standard in Brazil).

This means that to type "â" I press the ^ key, which is actually Shift+~, then I press the letter to go under it, in this case, "a". If I want to type a standalone ^, I have to either press Shift+~ twice, or follow the Shift+~ with an invalid letter, such as "q" or space.

So typing a lot of code that uses ^ is very annoying. For example, to type "^section" you have to do: Shift+~, Space, S...

Additionally, and this is purely Apple's fault for being entirely deranged, on Macs you have to watch out even further, as it is possible to write both ^ and ˆ, depending on what you press. Shift+~ then space prints the lone diacritic, while Shift+~ then right arrow prints the ASCII ^ you've seen so far. Obviously, having to reach for the arrow keys for a common occurrence when typing is incredibly inconvenient, on top of the inconvenience described previously.

[+] paulsmith|3 years ago|reply
> Also, what about IDE plugins?

An LSP server for Pushup syntax is on the informal roadmap.

[+] whage|3 years ago|reply
This really seems like php. I also think that mixing UI with logic is a recipe for disaster and there is already php (and several others?) for that. I suppose the creators of such a thing have a decent knowledge of compilers and related domains, knowledge which really seems wasted on a project like this.
[+] fredrikholm|3 years ago|reply
Bravo! This makes me genuinely happy.

A sane, compiled template language for Go, integrated with a framework that pushes for HATEOAS via HTMX hits every row and column in my dream-webs-tack-bingo-card.

Looking forward to digging into this during the weekend.

[+] smoyer|3 years ago|reply
This is really cool - so cool that I hate to be a naysayer. We used to build pages this way and to a large degree moved away from this practice because the applications became to hard to test. At the other end of the spectrum is "Passive View" which allows for testing of the business logic and easy mocking of the view - https://martinfowler.com/eaaDev/PassiveScreen.html.
[+] Eun|3 years ago|reply
Nice seeing going this forward. I built something similar in the past, that can be used as a base for such framework utilizing yaegi (from the creators of traefik): https://github.com/Eun/yaegi-template
[+] thefounder|3 years ago|reply
I don't like it. It looks like early php. This doesnt mean it is bad.

I'm working on a web framework as well based on wasm in the browser. It's perfect except it's not really usuable/production ready due to high memory consumption of the go->wasm implementation. My hope is that one day there will be support for GC in wasm and somehow the issue will go away.

[+] paulsmith|3 years ago|reply
Since Pushup is a compiler, one thought I've had is giving the ability to demarcate server-side and client-side code in a Pushup page, and compiling down to WASM for the client-side code. Then the compiler would generate the network code to do the hand-offs.
[+] aatd86|3 years ago|reply
Interesting! Do you have a link?
[+] euroderf|3 years ago|reply
Looks cool! Two questions:

What does a Treeview look like - in a ".up" file, can I walk a tree (like io.fs.FS) ?

Can I compile a Pushup app into a giant WASM hairball and run the whole thing in the browser ?

[+] paulsmith|3 years ago|reply
> What does a Treeview look like - in a ".up" file, can I walk a tree (like io.fs.FS) ?

Yes, you can just write normal Go code to traverse a filesystem and do with it whatever you like.

> Can I compile a Pushup app into a giant WASM hairball and run the whole thing in the browser ?

I haven't tried this so I don't know what would happen, and a lot would depend on what kind of JS host runtime support there was for anything that is doing a syscall.

But related to WASM, an idea I have is to add syntax to be able to split a Pushup page into client-side and server-side code, compile the client-side code to WASM, and use the Pushup compiler to generate the appropriate handoffs for any network calls between (AJAX, websocket, etc.)

[+] synergy20|3 years ago|reply
I like this project to succeed but I can hardly like the ^ for template, please make it similar to PHP|django etc if possible.
[+] nprateem|3 years ago|reply
Is there a decent auth library for go similar to what django provides, or do people just use keycloak for user accounts?
[+] Gys|3 years ago|reply
Very interesting! Kind of react, but in Go. Fingers crossed for 'Pushup Native' for mobile ;-)