Creare una web api in Go senza framework
Per Go esistono parecchi web framework molto validi, e ne abbiamo anche parlato in diverse occasioni.
Solo che Go nasce già con tutto il necessario per creare web api/REST service senza usare altri framework.
Ovviamente usare o no frameworl ha i suoi vantaggi a svantaggi.
Comunque oggi vi propongo un pò di codice.
L'unica libreria da installare è il driver per MariaDB/MySQL; ovviamente non è obbligatorio, dipende da che db volete usare:
go get -u github.com/go-sql-driver/mysql
Qui sotto il codice; ho messo tutto insieme per comodità:
package main
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
_ "github.com/go-sql-driver/mysql"
)
// Strutture dati
type Persona struct {
ID int `json:"id"`
Email string `json:"email"`
}
type Response struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error string `json:"error,omitempty"`
}
var db *sql.DB
func initDB() error {
var err error
dsn := "root:9211@tcp(localhost:3306)/test"
db, err = sql.Open("mysql", dsn)
if err != nil {
return fmt.Errorf("errore apertura database: %v", err)
}
if err = db.Ping(); err != nil {
return fmt.Errorf("errore connessione database: %v", err)
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
log.Println("Connessione al database MariaDB riuscita")
return nil
}
func respondJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
func personeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
path := strings.TrimPrefix(r.URL.Path, "/persone")
switch r.Method {
case "GET":
if path == "" || path == "/" {
// GET /persone - Lista tutte le persone
handleGetAllPersone(w, r)
} else {
// GET /persone/:id - Ottieni una persona specifica
id := strings.TrimPrefix(path, "/")
handleGetPersona(w, r, id)
}
case "POST":
// POST /persone - Crea nuova persona
handleCreatePersona(w, r)
default:
respondJSON(w, http.StatusMethodNotAllowed, Response{
Success: false,
Error: "Metodo non supportato",
})
}
}
func handleGetAllPersone(w http.ResponseWriter, r *http.Request) {
rows, err := db.Query("SELECT id, email FROM persone")
if err != nil {
log.Printf("Errore query: %v", err)
respondJSON(w, http.StatusInternalServerError, Response{
Success: false,
Error: "Errore durante il recupero dei dati",
})
return
}
defer rows.Close()
persone := []Persona{}
for rows.Next() {
var p Persona
if err := rows.Scan(&p.ID, &p.Email); err != nil {
log.Printf("Errore scan: %v", err)
continue
}
persone = append(persone, p)
}
if err := rows.Err(); err != nil {
log.Printf("Errore rows: %v", err)
respondJSON(w, http.StatusInternalServerError, Response{
Success: false,
Error: "Errore durante il recupero dei dati",
})
return
}
respondJSON(w, http.StatusOK, Response{Success: true, Data: persone})
}
// GET /persone/:id - Ottieni una persona
func handleGetPersona(w http.ResponseWriter, r *http.Request, id string) {
var p Persona
err := db.QueryRow("SELECT id, email FROM persone WHERE id = ?", id).Scan(&p.ID, &p.Email)
if err == sql.ErrNoRows {
respondJSON(w, http.StatusNotFound, Response{
Success: false,
Error: "Persona non trovata",
})
return
}
if err != nil {
log.Printf("Errore query: %v", err)
respondJSON(w, http.StatusInternalServerError, Response{
Success: false,
Error: "Errore durante il recupero dei dati",
})
return
}
respondJSON(w, http.StatusOK, Response{Success: true, Data: p})
}
// POST /persone - Crea nuova persona
func handleCreatePersona(w http.ResponseWriter, r *http.Request) {
var p Persona
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
respondJSON(w, http.StatusBadRequest, Response{
Success: false,
Error: "JSON non valido",
})
return
}
if p.Email == "" {
respondJSON(w, http.StatusBadRequest, Response{
Success: false,
Error: "Email obbligatoria",
})
return
}
result, err := db.Exec("INSERT INTO persone (email) VALUES (?)", p.Email)
if err != nil {
log.Printf("Errore insert: %v", err)
respondJSON(w, http.StatusInternalServerError, Response{
Success: false,
Error: "Errore durante la creazione",
})
return
}
lastID, err := result.LastInsertId()
if err != nil {
log.Printf("Errore LastInsertId: %v", err)
respondJSON(w, http.StatusInternalServerError, Response{
Success: false,
Error: "Errore durante la creazione",
})
return
}
p.ID = int(lastID)
respondJSON(w, http.StatusCreated, Response{Success: true, Data: p})
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
respondJSON(w, http.StatusOK, Response{
Success: true,
Data: "Versione di test",
})
}
func main() {
if err := initDB(); err != nil {
log.Fatalf("Impossibile connettersi al database: %v", err)
}
defer db.Close()
http.HandleFunc("/", rootHandler)
http.HandleFunc("/persone", personeHandler)
http.HandleFunc("/persone/", personeHandler)
port := ":8080"
fmt.Printf("Server in ascolto su http://localhost%s\n", port)
fmt.Println("Endpoints disponibili:")
fmt.Println(" GET http://localhost:8080/persone")
fmt.Println(" GET http://localhost:8080/persone/:id")
fmt.Println(" POST http://localhost:8080/persone")
log.Fatal(http.ListenAndServe(port, nil))
}
Per avviarlo:
go run .
2025/12/02 08:41:50 Connessione al database MariaDB riuscita
Server in ascolto su http://localhost:8080
Endpoints disponibili:
GET http://localhost:8080/persone
GET http://localhost:8080/persone/:id
POST http://localhost:8080/person
Qui sotto gli esempi con curl:
# Lista tutte le persone
curl http://localhost:8080/persone
# Crea una persona
curl -X POST http://localhost:8080/persone \
-H "Content-Type: application/json" \
-d '{"email":"mario@example.com"}'
# Ottieni una persona specifica
curl http://localhost:8080/persone/1
Come compito vi lascio quello di aggiungere anche i metodi PUT e DELETE.
Enjoy!
go rest database net
Commentami!