Day 50 to 60 - New year, clear horizons
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.
On the other hand, I was also able to finish the initial setup for my local proxmox server to be remotely managed via Ansible.