Back again with another mini-project, this time with a calculator backend API tool written in Go!
I recently wrapped up a project to build an HTTP+JSON API for a stateless calculator, and let me tell you—it was a lot more fun (and a little more challenging) than I expected. On the surface, it might sound simple: “Hey, it’s just a calculator, right?” But once you dive in, you start uncovering all the nitty-gritty details that make an API truly production-ready.
So, let me share the process, the lessons learned, and what I’d tweak if I had to do it again.
~ Souce code: Found
I hadn’t initially planned for this API to be used in a browser-based app, but as the project evolved, it became clear that CORS (Cross-Origin Resource Sharing) was necessary.
Using the github.com/rs/cors package, I quickly added the required headers to allow browser-based clients to interact with the API.
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "OPTIONS"},
AllowedHeaders: []string{"Content-Type"},
AllowCredentials: true,
})
My main packages
1) net/http: Go’s standard library for setting up the HTTP server and routing requests.
2) encoding/json: To handle JSON encoding/decoding for requests and responses.
3) golang.org/x/exp/slog: For logging every request in either text or JSON format.
4) github.com/rs/cors: To handle cross-origin requests for web integrations.
What about the calculation logic?
Well I made some handlers for these calculations, they are pretty basic in nature and primarily focus on the core functionality of calculations (Addition, Subtraction, Division & Multiplication).
//handlers.go
func AddHandler(logger *slog.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
handleOperation(logger, w, r, func(a, b float64) float64 { return a + b })
}
}
func SubtractHandler(logger *slog.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
handleOperation(logger, w, r, func(a, b float64) float64 { return a - b })
}
}
func MultiplyHandler(logger *slog.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
handleOperation(logger, w, r, func(a, b float64) float64 { return a * b })
}
}
func DivideHandler(logger *slog.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
handleOperation(logger, w, r, func(a, b float64) float64 {
if b == 0 {
panic("division by zero")
}
return a / b
})
}
}
Aside from this, handling my HTTP requests with the famous net/http package was straightforward and there are many examples out there of doing this in different ways. My case was very simple and I just needed to serve up some HTTP, handling the response writer and the request.
func LoggingMiddleware(next http.Handler, logger *slog.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
lrw := &loggingResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(lrw, r)
duration := time.Since(start)
logger.Info("Request completed",
"path", r.URL.Path,
"method", r.Method,
"status", lrw.statusCode,
"duration", duration)
})
}
type loggingResponseWriter struct {
http.ResponseWriter
statusCode int
}
func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}
Here are some use-case examples:
// Start up server
// go run main.go
time=2024-12-01T19:11:47.777-05:00 level=INFO msg="Starting server on :3000"
time=2024-12-01T19:11:55.440-05:00 level=INFO msg="Request completed" path=/add method=POST status=200 duration=164.584µs
time=2024-12-01T19:12:51.520-05:00 level=INFO msg="Request completed" path=/subtract method=POST status=200 duration=124.875µs
time=2024-12-01T19:12:56.727-05:00 level=INFO msg="Request completed" path=/divide method=POST status=200 duration=40.875µs
time=2024-12-01T19:13:04.128-05:00 level=INFO msg="Request completed" path=/multiply method=POST status=200 duration=46.458µs
Requests:
// Addition
curl -d '{"a": 10, "b": 5}' http://localhost:3000/add
{"result":15}
// Subtraction
curl -d '{"a": 10, "b": 5}' http://localhost:3000/subtract
{"result":5}
// Division
curl -d '{"a": 10, "b": 5}' http://localhost:3000/divide
{"result":2}
// Multiplication
curl -d '{"a": 10, "b": 5}' http://localhost:3000/multiply
{"result":50}
Conclusion
Yep! That's pretty much it! It was a quick and fun mini-project that I enjoyed! Feel free to give it a try and improve & enhance whichever way you'd like to.
Until next time, cheers! 🍻 🥂
SOCIAL SHARE CARD GENERATOR