package main import ( "database/sql" "encoding/json" "fmt" "io/ioutil" "net/http" "time" _ "github.com/lib/pq" ) //Go Best practices: https://github.com/golovers/effective-go type Item struct { Id int32 `json:"id"` Name int32 `json:"name"` ValueCents int32 `json:"valueCents"` PurchaseCostCents int32 `json:"purchasedCostCents"` Aquired time.Time `json:"aquired"` Description string `json:"description"` Notes string `json:"notes"` SoldForCents int32 `json:"soldForCents"` SoldTo string `json:"soldTo"` Category Category `json:"category"` Subcategory Subcategory `json:"subcategory"` } type Category struct { Id int32 `json:"id"` Name string `json:"name"` } type Subcategory struct { Id int32 `json:"id"` Name string `json:"name"` CategoryId int32 `json:"categoryId"` } func main() { http.HandleFunc("/categories", categories) err := http.ListenAndServe(":8080", nil) if err != nil { panic(err) } } func categories(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": getCategories(w, r) case "POST": insertCategory(w, r) default: w.WriteHeader(http.StatusMethodNotAllowed) w.Write([]byte("Method not allowed")) } } func getCategories(w http.ResponseWriter, r *http.Request) { //Note: The postgres driver seems to get confused when no password is supplied, so omit it in the connection sting. //https://rajyavardhan.medium.com/when-you-get-relation-does-not-exist-in-postgres-7ffb0c3c674b psqlInfo := fmt.Sprintf("host=%s port=%d user=%s dbname=%s sslmode=disable", "localhost", 5432, "postgres", "inv") db, err := sql.Open("postgres", psqlInfo) if err != nil { panic(err) } defer db.Close() rows, err := db.Query("SELECT * FROM category;") if err != nil { panic(err) } defer rows.Close() var categories []Category for rows.Next() { var category Category if err = rows.Scan(&category.Id, &category.Name); err != nil { println(err.Error()) } categories = append(categories, category) } jsonBytes, err := json.Marshal(categories) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Failed to get a list of categories.")) return } w.WriteHeader(http.StatusOK) w.Write(jsonBytes) } func insertCategory(w http.ResponseWriter, r *http.Request) { bodyBytes, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Failed to parse request body")) return } var category Category err = json.Unmarshal(bodyBytes, &category) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Failed to unmarshal JSON")) return } psqlInfo := fmt.Sprintf("host=%s port=%d user=%s dbname=%s sslmode=disable", "localhost", 5432, "postgres", "inv") db, err := sql.Open("postgres", psqlInfo) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Failed to connect to the DB")) return } id := 0 err = db.QueryRow("INSERT INTO category (category_name) VALUES ($1) RETURNING category_id", category.Name).Scan(&id) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprint("New Record: ", id))) }