Cover Page
Backend Page
Go with Echo
Install Go
-
sshto your server and download the latest version of Go: Check Go’s Downloads page for the current latest version. As of the time of writing, the latest version is 1.25.0.server$ cd /tmp server$ wget https://go.dev/dl/go1.25.0.linux-amd64.tar.gz
We require Go version 1.18 or later. -
Go to Go’s download and install page.
-
Skip the first section on
Go download, you’ve already downloaded Go. -
Click on the
Linuxtab in the second (Go install) section of the instructions. - Follow the instructions, with the following modifications to step 1 (you need to be root to modify
/usr/local):server$ sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.25.0.linux-amd64.tar.gz server$ sudo chmod -R go+rX /usr/local/go
To update to a later version of Go, repeat the instructions above—which would
have you manually delete the existing Go folder (usually /usr/local/go/),
so don’t put any custom files in there.
chatterd module
First create and change into a directory where you want to keep your chatterd module:
server$ mkdir ~/reactive/chatterd
server$ cd ~/reactive/chatterd
Create a Go module called chatterd:
server$ go mod init chatterd
# output:
go: creating new go.mod: module chatterd
Create a file called main.go:
server$ vi main.go
We will put the server and URL routing code in main.go, starting with the following import lines:
package main
import (
"log"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
We next create a Route struct to hold the URL routing information needed by Echo and declare a global variable routes to hold an array of Routes. We route this endpoint to the llmprompt() function. With each route, we also specify which HTTP method is allowed for that URL endpoint:
type Route struct {
HTTPMethod string
URLPath string
URLHandler echo.HandlerFunc
}
var routes = []Route {
{"POST", "/llmprompt/", llmprompt},
}
The function llmprompt() will be implemented in handlers.go later.
For now, staying in main.go, in our main() function, we set up the Echo server:
- disable the
Echobanner printing on the console, - load up the
routesarray above, - set it to automatically reroute any URL specified without a trailing ‘/’ to one with trailing ‘/’, for example
https://YOUR_SERVER_IP/llmpromptwill be routed tohttps://YOUR_SERVER_IP/llmprompt/, - bind it to the default HTTPS port (443),
- assign it the
chatterdcertificate and key we created earlier, and . . .
launch the server:
func main() {
server := echo.New()
server.HideBanner = true
for _, route := range routes {
server.Match([]string{route.HTTPMethod}, route.URLPath, route.URLHandler)
}
server.Pre(middleware.AddTrailingSlash())
log.Fatal(server.StartTLS(":443",
"/home/ubuntu/reactive/chatterd.crt",
"/home/ubuntu/reactive/chatterd.key"))
}
We’re done with main.go. Save and exit the file.
handlers.go
We implement the URL path API handler in handlers.go:
server$ vi handlers.go
Start the file with the following imports and the Ollama base URL string:
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
"github.com/labstack/echo/v4"
)
We add a logging function to print to console results of handling each HTTP request:
func logOk(c echo.Context) {
log.Println("[Echo] |", http.StatusOK, `|`, c.RealIP(), `|`, c.Request().Method, c.Request().RequestURI)
}
We next set the Ollama base URL and specify the handler llmprompt(), which
simply forwards user prompt from the client to Ollama’s generate API and passes
through Ollama’s reply NDJSON stream to the client using Go’s httputil.NewSingleHostReverseProxy(), which we instantiate once as a global variable:
var OLLAMA_BASE_URL, _ = url.Parse("http://localhost:11434/api")
var proxy = httputil.NewSingleHostReverseProxy(OLLAMA_BASE_URL)
func llmprompt(c echo.Context) error {
req := c.Request()
req.Host = OLLAMA_BASE_URL.Host
req.URL.Path = "/generate"
proxy.ServeHTTP(c.Response(), req)
logOk(c)
return nil // do NOT call any other Echo responder!
}
We’re done with handlers.go. Save and exit the file.
Build and test run
To build your server:
server$ go get # -u # to upgrade all packages to the latest version
server$ go build
![]()
Go is a compiled language, like C/C++ and unlike Python, which is an interpreted language. This means you must run go build each and every time you made changes to your code, for the changes to show up in your executable.
To run your server:
server$ sudo ./chatterd
# Hit ^C to end the test
If you had
Preforkenabled and wanted to prefork 3 servers, do instead:server$ sudo GOMAXPROCS=3 ./chatterd
You can test your implementation following the instructions in the Testing Chatter APIs section.
References
-
Working with Go
- Golang tutorial series gentle and clear, though a bit out of date in parts now.
- Running multiple HTTP servers in Go
- Making a RESTful JSON API in Go
- Golang Json Marshal Example
- HTTP Status Code
- Effective Error Handling in Golang
- URL
- Single Host Reverse Proxy
| Prepared by Chenglin Li, Xin Jie ‘Joyce’ Liu, Sugih Jamin | Last updated August 24th, 2025 |