Cover Page

Backend Page

Go with Echo

Install 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:

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

:point_right: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


Prepared by Chenglin Li, Xin Jie ‘Joyce’ Liu, Sugih Jamin Last updated August 24th, 2025