Hostinger

API REST en GO

Introduction à l’API REST et à Go

Qu’est-ce qu’une API REST ?

  • API : interface pour communiquer avec un service ou une application de manière automatisée.

  • REST : style d’architecture web, où l’on utilise généralement HTTP pour effectuer des opérations CRUD (Create, Read, Update, Delete).

Pourquoi utiliser Go pour une API REST ?

Go est langage rapide et compilé. Il est ainsi idéal pour des services performants.

Création d'un serveur HTTP minimal

Créer le serveur

Nous avons appris à créer un serveur web dans la leçon précédente.

Nous allons en créer un autre pour notre API.

package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Bienvenue sur mon API Go !")
	})

	fmt.Println("Le port 8080 est utilisé pour lancer l'API Go !")
	http.ListenAndServe(":8080", nil)
}

Tester le serveur

go run main.go

Dans le navigateur de votre choix, si vous tapez l'url localhost:8080, vous devriez voir :

Lancer l'API grâce à Go (Golang)

Manipuler des données (struct, JSON)

Imaginons qu’on veuille manipuler des "items". On définit un type Item grâce à struct:

type Item struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

Les tags json:"..." indiquent comment Go encode/décode cette struct en JSON.

On stocke ensuite les items dans une slice.

var items = []Item{
    {ID: 1, Name: "Item A"},
    {ID: 2, Name: "Item B"},
}

On aura 2️⃣ items.

Gérer des endpoints

Qu'est-ce qu'un endpoints ?

Un endpoint est un point d’accès à une API auquel est associé une méthode HTTP (GET, POST, PUT, DELETE, etc...) et une "URL", afin que le client puisse envoyer une requête précise (par exemple GET /items) pour réaliser une action donnée.

GET : Récupérer la liste des items

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

type Item struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

var items = []Item{
	{ID: 1, Name: "Item A"},
	{ID: 2, Name: "Item B"},
}

func itemsHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		// On renvoie la liste en JSON
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(items)
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Méthode non autorisée"))
	}
}

func main() {

	http.HandleFunc("/", itemsHandler)

	fmt.Println("Le port 8080 est utilisé pour lancer l'API Go !")
	http.ListenAndServe(":8080", nil)

}

Voici comment comprendre le code (ligne 19 à 27).

r.method permet de vérifier la méthode : GET, POST, ...

json.NewEncoder(w).Encode(items) encode items en JSON et l’envoie dans la réponse.

w.Header().Set("Content-Type", ...) : indique que la réponse est au format JSON.

Rappelez-vous que w correspond à la réponse que nous envoyons.

Lorsque vous tapez l'url localhost:8080 dans votre navigateur, le résultat suivant apparaîtra :

[
  {
    "id":1,
    "name":"Item A"
  },
  {
    "id":2,
    "name":"Item B"
  }
]

Le serveur vous envoie des données sous forme JSON.

POST : Ajouter un nouvel item

On va maintenant utiliser utiliser la méthode POST pour ajouter un élément.

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

type Item struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

var items = []Item{
	{ID: 1, Name: "Item A"},
	{ID: 2, Name: "Item B"},
}

func itemsHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodPost:
		w.Header().Set("Content-Type", "application/json")

		var newItem Item
		err := json.NewDecoder(r.Body).Decode(&newItem)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			json.NewEncoder(w).Encode(map[string]string{"error": "Données invalides"})
			return
		}

		// Générer un ID (simplement la longueur + 1, par exemple)
		newItem.ID = len(items) + 1
		items = append(items, newItem)

		// Répondre avec l'item créé
		w.WriteHeader(http.StatusCreated)
		json.NewEncoder(w).Encode(newItem)

	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Méthode non autorisée"))
	}
}

func main() {

	http.HandleFunc("/", itemsHandler)

	fmt.Println("Le port 8080 est utilisé pour lancer l'API Go !")
	http.ListenAndServe(":8080", nil)

}

Nous allons expliquer ce code.

Ligne 24var newItem Item : Nous déclarons la variable newItem de type Item. Ce type, généralement une struct, va nous permettre de stocker les informations que nous allons extraire du corps (body) de la requête. Cela nous permettra de récupérer la nouvelle donnée.

Ligne 25err := json.NewDecoder(r.Body).Decode(&newItem) : Nous créons ensuite un décodeur JSON basé sur r.Body, qui contient le contenu de la requête HTTP au format JSON. Avec la méthode Decode(&newItem), nous demandons à ce décodeur de convertir le JSON reçu en un objet Item. Le résultat de cette opération peut produire une erreur (par exemple si le JSON est mal formé), c’est pourquoi nous stockons cette éventuelle erreur dans la variable err.

Ligne 26if err != nil { ... } : Nous vérifions si la variable err n’est pas nulle, ce qui signifierait que la phase de décodage a échoué. Si err != nil, alors quelque chose s’est mal passé, soit parce que le JSON n’est pas valide, soit parce qu’il ne correspond pas à la structure Item.

Ligne 27w.WriteHeader(http.StatusBadRequest) : Si l’erreur err indique effectivement un problème, nous renvoyons au client un code HTTP 400 (Bad Request). Cela signifie que la requête était invalide ou mal formulée, ce qui donne une indication claire au client de la nature de l’erreur.

Ligne 28json.NewEncoder(w).Encode(map[string]string{"error": "Données invalides"}) : Après avoir spécifié le code d’erreur, nous répondons également avec un petit JSON qui contient une clé "error" et la valeur "Données invalides". Cette réponse est encodée à l’aide de json.NewEncoder(w).Encode(...), ce qui permet au client (le navigateur) de comprendre la raison de l’échec.

Ligne 29return : Nous utilisons return pour sortir immédiatement de la fonction handler, car nous ne voulons pas poursuivre le traitement si les données envoyées par le client ne sont pas conformes.