Cover Page
Backend Page
Go with Echo
Install Go
-
ssh
to 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
Linux
tab 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 Route
s. 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
Echo
banner printing on the console, - load up the
routes
array above, - set it to automatically reroute any URL specified without a trailing ‘/’ to one with trailing ‘/’, for example
https://YOUR_SERVER_IP/llmprompt
will be routed tohttps://YOUR_SERVER_IP/llmprompt/
, - bind it to the default HTTPS port (443),
- assign it the
chatterd
certificate 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
Prefork
enabled 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 |