custom header

Day 50 - Half way through!! - 10/01/2025

To start off the year officially, and with all my previous steps/projects already finished, I am now learning how to code a MySQL CRUD API. For this task I am also using the local Debian 12 VM I did set up for the database connection tutorial.

Here it is my current initial code:

package main


import (

"database/sql"
"log"
"net/http"

"github.com/go-sql-driver/mysql"

)

type Album struct {

	ID int
	Title string
	Artist string
	Price float32
}

func main() {

	cfg := mysql.Config{

		User: "mysqlu", //This value were hardcoded 
		Passwd: "pwd", // this one too
		User: os.Getenv("DBUSER"),
		Passwd: os.Getenv("DBPASS"),
		Net: "tcp",
		Addr: "192.168.1.134:3306",
		DBName: "MUSIC",

	}

	r := http.NewServeMux()
	r.HandleFunc("GET /albums", getAlbums)
	log.Print("Starting server on port :8080...")
	log.Fatal(http.ListenAndServe(":8090", r))
}

  

func getAlbums(w http.ResponseWriter, r *http.Request) {

	// Get a database Handle -- Initialize DB connection

	db, err := sql.Open("mysql", cfg.FormatDSN())
	if err != nil {
	log.Fatal(err)
	}

}

Day 51 - 12/01/2025 && 14/01/2025

More methods! I got the Read method for my CRUD API done as well as the Create one! Here are the functions isolated. Wanna check the code?

func getAlbums(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var albums []Album
		rows, err := db.Query("SELECT * FROM Albums")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer rows.Close()

		for rows.Next() {
			var alb Album
			if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			albums = append(albums, alb)
		}

		if err := rows.Err(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		if len(albums) > 0 {
			if err := json.NewEncoder(w).Encode(albums); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		} else {
			w.Write([]byte("No albums found"))
		}
	}
}

func addAlbum(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var alb Album
		err := json.NewDecoder(r.Body).Decode(&alb)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		result, err := db.Exec("INSERT into Albums VALUES ((select max(ID)+1 from Albums a),?, ?, ?);", alb.Title, alb.Artist, alb.Price)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

		id, err := result.LastInsertId()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		alb.ID = int(id)
		json.NewEncoder(w).Encode(alb)
	}
}

/*
curl http://localhost:8090/albums/add \
    --include \
    --header "Content-Type: application/json" \
    --request "POST" \
    --data '{"Title": "The Modern Sound of Betty Carter","Artist": "Betty Carter","Price": 49.99}'

*/

Day 52 - 16/01/2025

More functionality for the API! This time, I implemented (and tested) the Update method, as of now it went pretty much as reviewing other API examples and imitating the Insert function from before.

func updateAlbum(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var alb Album
		err := json.NewDecoder(r.Body).Decode(&alb)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		_, err = db.Exec("UPDATE Albums SET Title = ? WHERE ID = ?", alb.Title, alb.ID)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}

/*
curl http://localhost:8090/albums/update \
    --include \
    --header "Content-Type: application/json" \
    --request "PUT" \
    --data '{"ID" : 9,"Title": "Test"}'

*/

The expected behavior is to parse the curl request with a JSON body and take the values as arguments for the UPDATE statement.

Day 53 - 17/01/2025

Making the last basic functionality for the CRUD API has been more complex, at first I expected it to be really similar to the UPDATE method, but for the DELETE one, it required me to check about HTTP methods and other MySQL APIs workarounds, yet I ended up asking to the community and here it is the end up result:

func deleteAlbum(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var alb Album
		err := json.NewDecoder(r.Body).Decode(&alb)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		_, err = db.Exec("DELETE FROM Albums WHERE ID = ?", alb.ID)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	}
}

/*
  -- REQUIRES TO AVOID ESCAPING LINES
	curl -X DELETE http://localhost:8090/albums/delete -H "Content-Type: application/json" -d '{"ID" : 8}'
*/

It is worth mentioning that it took quite some time to make this work, because of a rather funny (or exhausting) mistake, since the new lines were escaped, it didn’t interpret the instruction properly, once it was placed on a single line, the curl command worked.

Overall I know that this is a pretty basic API, I could add more specific INSERT statements as well as UPDATE or DELETE ones, so I prefer to pivot into a new project, or rather a new approach to the same one.

Days 54 - 55 - 56 - 18/01/2025 - 22/01/2025 - 30/01/2025

After spending time coding and finishing some projects, I’ve been investing the last two week (Just pointing three days because is a rather more justified and not unfair way to show how much time I’ve spent on this) looking around for which project to commit to next, for which I stumbled upon htmx and website projects. The core idea is to develop a mutable html file with more flexible components, and during this journey, refresh some of my knowledge with web development, may be even expanding to know the basics of a framework such as Tailwind CSS.

In any case, just to not go too far and lose track of the scale of this project, I will stick as much as I can to a project concept and keep things simple aesthetic-wise.

Some resources I found & stored along the way:

Codez Up - Creating a real-time chat application with Golang and Websockets

Medium - Simple chat application with Golang and Websocket

Youtube - Generic search for chat application with Golang

Package main - Youtube channel

Alongside other go-related yet just for curiosity articles Go tool - Go version 1,24

Day 57 - 05/02/2025

Today I took a breather of preparing my next project and went ahead about finishing the App Engine introductory labs for Google Cloud, this is of importance to me since one of them included the Golang language, so I could refresh how to deploy and use the GCP component in on itself.

Source

On the other hand, I was also able to finish the initial setup for my local proxmox server to be remotely managed via Ansible.

Terminal Ansible screenshot