Musings Of Faking a REST-API Client in Server-Side Rendered Templates
One of the arguments proponents have against server-side rendering is that you can have a well-defined API that would work for both your web frontend and things like a mobile client. It’s a good argument, and it got me wondering if it’s possible to do this with server-side templates too.
What I’m imagining is a series of Go handlers powering the API, and another series of Go handlers that simply serve templates, all being served by one app. These templates would have what look like HTTP calls, like “get” and “post”, but in the background, will simply call the API handlers with a “fake” HTTP request. It would look like REST calls from the templates, but under the hood, it would be handers calling handlers directly, without a network round-trip.
If you imagine, for example, an API for getting a list of support tickets, maybe with the following REST calls:
GET /tickets/- list all tickets with an optional search queryGET /tickets/{:id}- get details of a single ticketPOST /tickets/- create a new ticket
Then, if you want to build a homepage showing the user’s currently assigned tickets, you could build a Go template like the following:
<h1>My tickets</h1>
{{ $tickets := get "/tickets?owner=me" }}
{{ range $tickets }}
<!-- Render tickets as HTML here -->
{{ end }}
That get call will appear like a HTTP request, but could simply be a http.Request created in memory, and sent directly to the handler via the ServeHTTP method. No need for a server round-trip, and you get the benefits of having a well-defined API along with the flexibility of a frontend that will use that API to the extent that it needs to.
It could potentially work with posts too. For example, the template accepting the form inputs for a new ticket:
{{ $myData := .FormData }}
{{ $res := post "/tickets" $myData }}
{{ send_redirect "/" }}
This does look a bit like PHP to my eyes if I’m being honest. I’m guessing you’ll need a pretty decent templating language to support this, but this wouldn’t be a problem for Go’s template.1
Not entirely sure why you’d would choose this route over simply rendering the templates in the handler. Since they’re in the same memory space, there’s nothing stopping you from simply passing domain models around. One argument might be that this will force you to have a well-defined API that you yourself are using. Another might be that should you ever want to split your system into an API and HTTP frontend, it would easy to do so: simply take the handlers serving these templates and place them in a separate binary. Naturally this all depends on how separate you’re keeping the two, so some discipline in making sure the two sets of handlers are isolated might be needed if this is a possibility.
You will loose niceties like having a path serving both an API and frontend (although, with proper content negotiation you can probably get away with it), and yeah, we are still talking about server-side rendering, so no virtual DOMs or all that “niceties” that come from React US. Even so, might be worth exploring as a technique for a small project, just to see how it feels.
-
Or an embedded PHP runtime, if you’re feeling adventurous. ↩︎