Piko
The Go web framework behind this website, grown from our client work.
The pieces we kept building
My wife and I build websites and tools for clients. The projects have their own requirements, but some jobs keep coming back. A page needs data, a form needs to save something, somebody needs to upload a file, and this client's storage needs a different provider from the one I used for the last client's. Piko is where I've been bringing those shared pieces together.
The storage work makes a useful example. A client's file manager needed cloud storage, our CMS used S3 and another project needed local disk. The application still needed to put, get and remove files. I could keep those everyday operations behind one interface while dealing with each provider's behaviour underneath.
The rendering project started as GoSSR in April 2024 because I was tired of keeping a generated TypeScript client in step with my Go types.
Rendering the page with those Go values lets the compiler check their use directly, with less translation for me to maintain.
The queries, storage and other shared tools needed a home too. Piko means small. I had a small, complete engine in mind, well, complete enough for the jobs we kept doing, and then it kept absorbing shared code from more of our client projects.
The name is optimistic.
One route through the application
A data-backed page joins those tools up. I write SQL and Seagull produces typed Go methods and records, and the page uses those records in its template. Seagull can also generate queries for Go applications outside Piko. While I'm editing, the interpreter runs the changing Go. I need that whole route to work before I can see the page, so it gives me an integration to test together.
I hate finding that one plugin wants a different version from the adapter beside it. My aim is for a Piko release to select compatible supplied parts, with clear interfaces for clients to replace them. A custom adapter still needs a Piko release to target. Bringing the tools together should spare me some version guessing.
What I'm aiming for is the development speed I associate with Node.js, with a compact compiled Go application to deploy. Change the page, save it, see the result.
Piko uses libraries underneath but I can take on choosing and connecting them for the framework. Each application can then use the supplied parts without rebuilding that collection.
A page I can follow
Say I'm showing a list of cities. Some Go produces the list and some markup displays it. Keeping the page's code beside its template means I can see both without following a handler into another file. Here is a complete little .pk page:
<template>
<p p-for="city in state.Cities" p-text="city.Name"></p>
</template>
<script type="application/x-go">
package main
import "piko.sh/piko"
type City struct { Name string }
type Response struct { Cities []City }
func Render(_ *piko.RequestData, _ piko.NoProps) (Response, piko.Metadata, error) {
return Response{Cities: []City{{"London"}, {"Paris"}}}, piko.Metadata{}, nil
}
</script>
The template reads Cities from the response and writes each name in a paragraph.
London
Paris
Putting them in one file is only the beginning. Piko compiles the template into Go source, so city.Name becomes a real field access that Go can check. Misspell the field and the build has something concrete to complain about, which is when I'd prefer to meet that typo, while I'm changing the page and still have the field in front of me.
I enjoy understanding what has to happen between writing a page and seeing it. Even this little file brings two languages together. I have to get their connection right, including escaping values for the HTML context they appear in.
In production the generated Go becomes part of the application's binary, along with its assets. The same template format also covers emails and PDF documents. The CV you can download is one of those PDFs.
Server-rendered pages aren't the whole site. Piko's .pkc format provides client-side components for interactive parts, including the figures in my articles. And then there is the house. It has rather different navigation requirements from a client file manager.
Anyway, that is the production version. While developing a page, I'm changing its Go as well as its markup. Rebuilding the application for each change gets in the way of seeing what happened.
That development loop led me into building an interpreter. The work has since grown into Pipit, with its own repository and embedding API.
Piko is open source and in alpha so its API still changes. The documentation and repository track current capabilities and installation.
More from building Piko
Putting up with storage
Put, get and remove look simple. Checksums, failed uploads and shared readers give me rather more to work through.
Love at first SELECT
I want to keep writing SQL. I'm trying a different design for dialects, type analysis and queries that change at runtime.
Putting Go in the template file
GoSSR keeps a page's Go and markup together so its data can stay in one type system.
Bringing it all together
Bringing my templates, queries and interpreter together so I can test and version them as one system.