2
\$\begingroup\$

I am a learner in Go and wrote example below in a day so I would like to get some suggestions to improve it. It is a simple rest api that currently just accepts request and returns a dummy message. My main aim here is to get comments and suggestions on how I structured the flow/logic. Although it currently works fine and easy to follow (at least to me), could it be done in a cleaner/scalable way?

cmd/server.go

package main

import (
    "fmt"
    "football-api/internal/app/route"
    "log"
    "net/http"
)

type server struct {}

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    response, err := route.Handler(r)
    if err != nil {
        w.WriteHeader(http.StatusNotFound)
    } else {
        w.WriteHeader(http.StatusOK)
    }

    w.Write([]byte(response))
}

func main() {
    fmt.Println("Server is running on port 8080 ...")

    err := http.ListenAndServe(":8080", &server{})
    if err != nil {
        log.Fatalf("Couldn't start server on :8080. [%s]", err.Error())
    }
}

internal/app/route/handler.go

package route

import (
    "errors"
    "net/http"
    "path/filepath"
)

func Handler(r *http.Request) (response string, err error) {
    switch filepath.Clean(r.URL.Path) {
    case "/":
        return Home(r), nil
    case "/comments":
        return Comments(r), nil
    default:
        return "", errors.New("route was not found")
    }
}

internal/app/route/home.go

package route

import "net/http"

func Home(r *http.Request) string {
    return "Home"
}

internal/app/route/comments.go

package route

import "net/http"

func Comments(r *http.Request) string {
    return "Comments"
}
\$\endgroup\$
1
\$\begingroup\$

There's not so much code that it would be hard to read, looks clean enough to me.

Regarding scalable ... in what way? The switch in Handler will be the limiting factor since adding another route will always have to have a corresponding edit in that function. Some frameworks will have ways around that (for better or worse), but as long as there are just a handful of routes I doubt it's a real issue (though if this is more of an exercise, go for it: if you have a hundred handlers, how could you register them instead of having a single point like here?).

In ServeHTTP there'll definitely be an issue with the need for additional, more nuanced error codes. I'm not suggesting a particular solution, but I can see that happening quite quickly ... plus, 404 for an internal error looks wrong too, that should be 500 most likely, only if the route couldn't be found 404 would be appropriate.


Okay two more, returning a string instead of directly writing output will be a scalability problem, it'd be much better to avoid additional memory allocations to construct that string and simply write to the output buffer / writer directly.

And, the result of a errors.New can be stored in a global variable as long as the message doesn't change - one more memory allocation gone.

\$\endgroup\$
  • 1
    \$\begingroup\$ scalable: I was thinking of making things decoupled but I since discovered DI in go its ok for now. I inject structs into dependent structs with constructors now. more routes: I'll have to deal with them manually for now but it shouldn't be problem adding a line for a new route. more status codes and content return: I'll follow your suggestion and inject ResponseWriter to route files to direct output there rather than returning. errors.New: I'll follow your suggestion. Thanks for the input. +1. I hope I understood your suggestions correctly. \$\endgroup\$ – BentCoder Aug 22 at 19:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.