Cover Page
Backend Page
Go with Echo
chatterd
Change to your chatterd
directory and edit the file main.go
:
server$ cd ~/reactive/chatterd
server$ vi main.go
Add the following import lines inside your import()
block:
"context"
"github.com/jackc/pgx/v4/pgxpool"
Continuing in main.go
, find the global variable router
and
add two routes right after the route for /llmprompt
. These serve Chatter
’s two APIs: HTTP GET request, with URL endpoint getchatts/
, and HTTP POST request, with endpoint postchatt/
. With each endpoint, we also specify which HTTP method is allowed for that URL endpoint and its handler function:
{"GET", "/getchatts/", getchatts},
{"POST", "/postchatt/", postchatt},
The functions getchatts()
and postchatt()
will be implemented in handlers.go
.
Staying in main.go
, set up a pool of open connections to the PostgreSQL chatterdb
database. Maintaining a pool of open connections avoids the cost of opening and closing a connection on every database operation. The password used in creating pgxpool
must match the one you used when setting up Postgres earlier. The connection pool needs to be accessible to all the URL handlers, so we store it in a global variable chatterDB
:
var background = context.Background()
var chatterDB *pgxpool.Pool
Then in the main()
function, add these lines right after the function signature:
var err error
chatterDB, err = pgxpool.Connect(background, "host=localhost user=chatter password=chattchatt dbname=chatterdb")
if err != nil { panic(err) }
We’re done with main.go
. Save and exit the file.
handlers.go
Edit handlers.go
:
server$ vi handlers.go
First add the following import to the import()
block:
"time"
We define a Chatt
struct to help postchatt()
deserialize JSON
received from clients. Add these line right below the import()
block:
type Chatt struct {
Username string `json:"username"`
Message string `json:"message"`
Id string `json:"id"`
Timestamp time.Time `json:"timestamp"`
}
The property names must be capitalized to be visible to the JSON deserializer.
Now add a number of logging functions that, in case of error, prepare a corresponding JSON response to be returned to the client:
func logServerErr(c echo.Context, err error) error {
log.Println("[Echo] |", http.StatusInternalServerError, `|`, c.RealIP(), `|`, c.Request().Method, c.Request().RequestURI, err.Error())
return c.JSON(http.StatusInternalServerError, err.Error())
}
func logClientErr(c echo.Context, sc int, err error) error {
log.Println("[Echo] |", sc, `|`, c.RealIP(), `|`, c.Request().Method, c.Request().RequestURI, err.Error())
return c.JSON(sc, err.Error())
}
The handler getchatts()
uses an open connection from the connection pool to query the database for stored chatts. Once all the rows from the database are retrieved, we insert the resulting array of chatts
into the response JSON object. Add getchatts()
to your handlers.go
after the definition of the llmprompt()
function.
func getchatts(c echo.Context) error {
var chattArr = [][]any{}
var chatt Chatt
rows, err := chatterDB.Query(background, `SELECT username, message, id, time FROM chatts`)
if err != nil {
if rows != nil { rows.Close() }
return logServerErr(c, err)
}
for rows.Next() {
err = rows.Scan(&chatt.Username, &chatt.Message, &chatt.Id, &chatt.Timestamp)
if err != nil {
rows.Close()
return logServerErr(c, err)
}
chattArr = append(chattArr, []any{chatt.Username, chatt.Message, chatt.Id, chatt.Timestamp})
}
logOk(c)
return c.JSON(http.StatusOK, chattArr)
}
Similarly, postchatt()
receives a posted chatt
in the expected JSON format, deserializes into the Chatt
struct, and inserts it into the database, using a connection from the pool. The UUID and time stamp of each chatt are generated at insertion time. Add postchatt()
to the end of your handlers.go
:
func postchatt(c echo.Context) error {
var chatt Chatt
if err := c.Bind(&chatt); err != nil {
return logClientErr(c, http.StatusUnprocessableEntity, err)
}
_, err := chatterDB.Exec(background, `INSERT INTO chatts (username, message, id) VALUES ($1, $2, gen_random_uuid())`, chatt.Username, chatt.Message)
if err != nil {
return logClientErr(c, http.StatusBadRequest, err)
}
logOk(c)
return c.JSON(http.StatusOK, struct{}{}) // empty struct instance serialized to empty JSON: {}
}
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
You can test your implementation following the instructions in the Testing Chatter
APIs section.
References
</details> </div>
Prepared by Sugih Jamin | Last updated July 25th, 2025 |