Construire une API REST en GO

Construire une API REST en GO

Le langage Go (ou Golang) est souvent choisi pour développer des API REST rapides, fiables et faciles à déployer. Combiné avec le framework Gin, il devient un outil redoutable pour créer des microservices légers et performants.

Présentation

Pour développer une API REST, notre architecture doit être posée dès le départ. Voici la structure à avoir :

go-gin-api/
├── main.go
├── go.mod
├── config/
│   └── config.go
├── controllers/
│   └── user_controller.go
├── models/
│   └── user.go
├── routes/
│   └── routes.go
└── services/
    └── user_service.go

Voici l'utilité de chaque répertoire :
models/ : définit les structures de données et le mapping DB.
controllers/ : gère la logique et les méthodes HTTP.
services/ : contient la logique métier.
routes/ : centralise les endpoints.
config/ : contient la config (port, DB, etc.).

Comme vous l'aurez vu, notre API va servir à de la gestion d'utilisateurs

Le modèle

Le modèle que nous allons créer est déterminant car il va imposer la structure de données que va retourner l'API REST.

Créer le fichier models/user.go :

package models

type User struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

Le service

Le service contient la logique métier : il s’occupe de la gestion et du traitement des données, indépendamment de la couche HTTP.
Dans notre cas, il s'agit de la gestion d'users. Il faut donc dire au service que l'ID de l'utilisateur est bien un entier et que l'utilisateur correspondant doit être retourné.

Créer le fichier services/user_service.go :

package services

import "go-gin-api/models"

var users = []models.User{
	{ID: 1, Name: "Alice", Email: "alice@example.com"},
	{ID: 2, Name: "Bob", Email: "bob@example.com"},
}

func GetUsers() []models.User {
	return users
}

func GetUserByID(id int) *models.User {
	for _, user := range users {
		if user.ID == id {
			return &user
		}
	}
	return nil
}

Le Controller

Le controller va faire le lien entre la requête HTTP et la logique métier. Il récupère les paramètres, appelle le service et formate la réponse.

Créer le fichier controllers/user_controller.go :

package controllers

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"go-gin-api/services"
)

func GetUsers(c *gin.Context) {
	c.JSON(http.StatusOK, services.GetUsers())
}

func GetUserByID(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
		return
	}
	user := services.GetUserByID(id)
	if user == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
		return
	}
	c.JSON(http.StatusOK, user)
}

Les routes

Les routes API sont un élément fondamental car ils définissent chaque endpoint, la méthode, ce qui est retourné etc...

Créer le fichier routes/routes.go :

package routes

import (
	"github.com/gin-gonic/gin"
	"go-gin-api/controllers"
)

func SetupRouter() *gin.Engine {
	r := gin.Default()

	api := r.Group("/api")
	{
		api.GET("/users", controllers.GetUsers)
		api.GET("/users/:id", controllers.GetUserByID)
	}

	return r
}

Main

Notre fichier principal main.go qui va démarrer l'application :

package main

import (
	"go-gin-api/routes"
)

func main() {
	r := routes.SetupRouter()
	r.Run(":8080")
}

Exécution

Lancer le serveur HTTP :

go run main.go

Récupérer la liste des utilisateurs :

curl http://localhost:8080/api/users
Réponse HTTP :
[
  {"id":1,"name":"Alice","email":"alice@example.com"},
  {"id":2,"name":"Bob","email":"bob@example.com"}
]

Récupérer l'utilisateur ayant l'ID 1 :

curl http://localhost:8080/api/users/1
Réponse HTTP :
{"id":1,"name":"Alice","email":"alice@example.com"}