Pourquoi API REST avec Go ?
Le développement d'API REST en Go est une pratique courante dans le monde moderne, offrant une solution efficace et performante pour les besoins des applications modernes. En tant que langage concurrentiel et rapide, Go permet de créer des serveurs web robustes et scalables qui peuvent gérer facilement une grande charge d'users en même temps.
Un cas d'usage concret est le développement d'une API de gestion de contenu pour un site web ou une application mobile. Cette API permettra aux utilisateurs de publier, modifier et supprimer des articles, tout en permettant aux visiteurs d'afficher ces publications.
Prerequis
- Connaissance de base du langage Go
- Installation de Go (version 1.17+ recommandée)
- Un éditeur de code Go (VSCode, GoLand, etc.)
- Connaissance des concepts fondamentaux de l'HTTP
Concepts fondamentaux
HTTP et les méthodes CRUD
Les API REST utilisent les méthodes HTTP pour effectuer des opérations sur les ressources. Les principales méthodes sont :
- GET : Récupère une ressource.
- POST : Crée une nouvelle ressource.
- PUT : Met à jour une ressource existante.
- DELETE : Supprime une ressource.
Voici comment ces méthodes peuvent être mappées dans un code Go :
// Création d'un handler pour la méthode GET
func getHandler(w http.ResponseWriter, r *http.Request) {
// Logique pour récupérer les données
fmt.Fprintf(w, "GET request received")
}
// Création d'un handler pour la méthode POST
func postHandler(w http.ResponseWriter, r *http.Request) {
// Logique pour créer une nouvelle ressource
fmt.Fprintf(w, "POST request received")
}
Serveur HTTP avec Go
Pour créer un serveur HTTP simple en Go, vous pouvez utiliser le package net/http.
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the API!")
})
http.ListenAndServe(":8080", nil)
}
Middleware et Logging
Un middleware est une fonction qui reçoit un http.Handler en paramètre, effectue des opérations avant ou après le traitement de la requête, puis appelle le prochain handler dans la chaîne.
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Handling request:", r.URL.Path)
next.ServeHTTP(w, r)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the API!")
})
server := &http.Server{
Addr: ":8080",
Handler: loggingMiddleware(mux),
}
server.ListenAndServe()
}
Mise en pratique : projet fil rouge
Pour ce tutoriel, nous allons créer un simple gestionnaire de tâches avec une API REST. Ce gestionnaire permettra d'ajouter des tâches, de les afficher et de les supprimer.
Étape 1 : Structure du Projet
Créez le répertoire pour votre projet et initialisez-le :
mkdir task-manager-api
cd task-manager-api
go mod init task-manager-api
Étape 2 : Modèle de Données
Créer un fichier models/task.go pour définir le modèle de données :
package models
type Task struct {
ID string `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
Étape 3 : Gestionnaire de Tâches
Créer un fichier services/task_service.go pour gérer les opérations sur les tâches :
package services
import (
"task-manager-api/models"
)
var tasks = []models.Task{
{ID: "1", Title: "Task 1", Completed: false},
}
func GetTasks() []models.Task {
return tasks
}
func AddTask(task models.Task) bool {
for _, t := range tasks {
if t.ID == task.ID {
return false
}
}
tasks = append(tasks, task)
return true
}
func DeleteTask(id string) bool {
for i, t := range tasks {
if t.ID == id {
tasks = append(tasks[:i], tasks[i+1:]...)
return true
}
}
return false
}
Étape 4 : Handlers
Créer un fichier handlers/task_handlers.go pour définir les handlers :
package handlers
import (
"encoding/json"
"net/http"
"task-manager-api/models"
"task-manager-api/services"
)
func GetTasksHandler(w http.ResponseWriter, r *http.Request) {
tasks := services.GetTasks()
json.NewEncoder(w).Encode(tasks)
}
func AddTaskHandler(w http.ResponseWriter, r *http.Request) {
var task models.Task
if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if services.AddTask(task) {
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(task)
} else {
http.Error(w, "Task ID already exists", http.StatusConflict)
}
}
func DeleteTaskHandler(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
if services.DeleteTask(id) {
w.WriteHeader(http.StatusOK)
} else {
http.Error(w, "Task not found", http.StatusNotFound)
}
}
Étape 5 : Configuration du Serveur
Créer un fichier main.go pour configurer le serveur :
package main
import (
"net/http"
"task-manager-api/handlers"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/tasks", handlers.GetTasksHandler)
mux.HandleFunc("/tasks", handlers.AddTaskHandler).Methods("POST")
mux.HandleFunc("/tasks?id=", handlers.DeleteTaskHandler).Methods("DELETE")
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
server.ListenAndServe()
}
Étape 6 : Exécution et Test
Exécutez le serveur :
go run main.go
Testez l'API avec curl ou Postman :
- GET http://localhost:8080/tasks
- POST http://localhost:8080/tasks -H "Content-Type: application/json" -d '{"id": "2", "title": "Task 2", "completed": false}'
- DELETE http://localhost:8080/tasks?id=2
Erreurs frequentes et debugging
Erreur : 405 Method Not Allowed
## ❌ Mauvais
mux.HandleFunc("/tasks", handlers.GetTasksHandler)
## ✅ Correct
mux.HandleFunc("/tasks", handlers.GetTasksHandler).Methods("GET")
Erreur : 500 Internal Server Error lors de l'ajout d'une tâche existante
## ❌ Mauvais
if services.AddTask(task) {
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(task)
}
## ✅ Correct
if added := services.AddTask(task); !added {
http.Error(w, "Task ID already exists", http.StatusConflict)
}
Erreur : 400 Bad Request lors de la suppression d'une tâche avec un ID invalide
## ❌ Mauvais
if services.DeleteTask(id) {
w.WriteHeader(http.StatusOK)
} else {
http.Error(w, "Task not found", http.StatusNotFound)
}
## ✅ Correct
id := r.URL.Query().Get("id")
if id == "" {
http.Error(w, "ID is required", http.StatusBadRequest)
return
}
if services.DeleteTask(id) {
w.WriteHeader(http.StatusOK)
} else {
http.Error(w, "Task not found", http.StatusNotFound)
}
Pour aller plus loin
- Gestion des erreurs avec Go: Apprendre comment gérer les erreurs de manière efficace en utilisant le package
errors. - Documentation Swagger: Utiliser Swagger pour générer automatiquement la documentation de l'API.
- Tests unitaires et d'intégration: Écrire des tests pour s'assurer que chaque fonctionnalité de l'API fonctionne correctement.
Défi pratique: Créez une API de blog simple avec les opérations CRUD pour les articles et utilisez Swagger pour documenter l'API.