Creare una web api in Go senza framework

Mattepuffo's logo
Creare una web api in Go senza framework

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!


Condividi

Commentami!