Go with Echo

Cover Page

Back-end Page

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 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 routing table to hold the URL routing information needed by Echo and assign it to a global variable routes. We define the routes to serve llmprompt’s two APIs: HTTP GET request with URL endpoint ‘/’ and HTTP POST request with URL endpoint /llmprompt. We route the first endpoint to the top() function and the second endpoint to the llmprompt() function. With each route, we specify which HTTP method is allowed for the URL endpoint, according to whether the endpoint accepts an HTTP GET or POST request:

type Route struct {
    HTTPMethod string
    URLPath    string
    URLHandler echo.HandlerFunc
}

var routes = []Route {
		{"GET", "/", top},	
		{"POST", "/llmprompt/", llmprompt},
}

The functions top() and llmprompt() will be implemented in handlers.go later.

For now, staying in main.go, in the main() function, 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 handlers in handlers.go:

server$ vi handlers.go

Start the file with the following imports:

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)
}

The top() handler for the server’s root ‘/’ API simply returns a JSON containing the string “EECS Reactive chatterd” and HTTP status code 200:

func top(c echo.Context) error {
	logOk(c)
	return c.JSON(http.StatusOK, "EECS Reactive chatterd")
}

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 NDJSON reply 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
}

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 llmPrompt APIs section.

References


Prepared by Chenglin Li, Xin Jie ‘Joyce’ Liu, Sugih Jamin Last updated January 7th, 2026