Construire des API REST avec Go et Fiber : Guide pratique pour débutants

Go est le langage derrière Docker, Kubernetes et la majorité de l'infrastructure cloud-native — et Fiber le rend accessible aux développeurs web. Inspiré par Express.js et construit sur Fasthttp, Fiber offre jusqu'à 10 fois le débit de Node.js avec la sécurité mémoire et la concurrence basée sur les goroutines. Dans ce tutoriel, vous construirez une API REST complète de zéro.
Ce que vous allez construire
Une API de gestion de tâches (Task Manager) complète avec :
- Opérations CRUD complètes pour les tâches (créer, lire, mettre à jour, supprimer)
- Traitement des requêtes et réponses JSON
- Base de données PostgreSQL avec GORM (l'ORM le plus populaire de Go)
- Gestion structurée des erreurs avec les codes HTTP appropriés
- Middlewares (journalisation, CORS, limitation de débit)
- Validation des entrées
- Configuration basée sur les variables d'environnement
- Tests d'intégration
Prérequis
Avant de commencer, assurez-vous d'avoir :
- Go 1.22+ installé via go.dev/dl
- PostgreSQL 15+ en local ou via Docker
- Une compréhension de base du protocole HTTP et des concepts REST
- Un éditeur de code (VS Code avec l'extension Go recommandé)
- Un terminal
Nouveau sur Go ? Vous devez être à l'aise avec les structs, les interfaces, la gestion des erreurs et les goroutines. Le Tour of Go officiel est un excellent point de départ.
Pourquoi Fiber ?
Avant de plonger dans le code, comprenons pourquoi Fiber se démarque dans l'écosystème Go :
| Caractéristique | Fiber | Gin | Echo | Chi |
|---|---|---|---|---|
| Inspiré par | Express.js | Martini | Sinatra | stdlib |
| Moteur HTTP | Fasthttp | net/http | net/http | net/http |
| Performance | Le plus rapide | Rapide | Rapide | Rapide |
| Courbe d'apprentissage | Faible (style Express) | Faible | Modérée | Faible |
| Middlewares | Intégrés + personnalisés | Communauté | Intégrés | Compatibles stdlib |
L'API de Fiber, similaire à Express, signifie que si vous avez déjà construit des serveurs Node.js, vous vous sentirez chez vous — mais avec les performances et la sécurité des types de Go.
Étape 1 : Configuration du projet
Créez le répertoire du projet et initialisez le module Go :
mkdir task-api && cd task-api
go mod init github.com/yourusername/task-apiInstallez les dépendances principales :
go get github.com/gofiber/fiber/v2
go get gorm.io/gorm
go get gorm.io/driver/postgres
go get github.com/joho/godotenvCréez la structure du projet :
mkdir -p cmd/api internal/{models,handlers,database,middleware,config}
touch cmd/api/main.go
touch .envVotre projet devrait ressembler à ceci :
task-api/
├── cmd/
│ └── api/
│ └── main.go
├── internal/
│ ├── config/
│ │ └── config.go
│ ├── database/
│ │ └── database.go
│ ├── handlers/
│ │ └── task.go
│ ├── middleware/
│ │ └── middleware.go
│ └── models/
│ └── task.go
├── .env
├── go.mod
└── go.sum
Étape 2 : Configuration de l'environnement
Configurez votre fichier .env :
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=taskapi
APP_PORT=3000Créez le chargeur de configuration dans internal/config/config.go :
package config
import (
"log"
"os"
"github.com/joho/godotenv"
)
type Config struct {
DBHost string
DBPort string
DBUser string
DBPassword string
DBName string
AppPort string
}
func LoadConfig() *Config {
err := godotenv.Load()
if err != nil {
log.Println("No .env file found, using system environment variables")
}
return &Config{
DBHost: getEnv("DB_HOST", "localhost"),
DBPort: getEnv("DB_PORT", "5432"),
DBUser: getEnv("DB_USER", "postgres"),
DBPassword: getEnv("DB_PASSWORD", "postgres"),
DBName: getEnv("DB_NAME", "taskapi"),
AppPort: getEnv("APP_PORT", "3000"),
}
}
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}Étape 3 : Définir le modèle de données
Créez le modèle Task dans internal/models/task.go :
package models
import (
"time"
"gorm.io/gorm"
)
type TaskStatus string
const (
StatusPending TaskStatus = "pending"
StatusInProgress TaskStatus = "in_progress"
StatusCompleted TaskStatus = "completed"
)
type Task struct {
ID uint `json:"id" gorm:"primaryKey"`
Title string `json:"title" gorm:"size:255;not null"`
Description string `json:"description" gorm:"type:text"`
Status TaskStatus `json:"status" gorm:"size:20;default:pending"`
Priority int `json:"priority" gorm:"default:0"`
DueDate *time.Time `json:"due_date,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}
type CreateTaskRequest struct {
Title string `json:"title"`
Description string `json:"description"`
Priority int `json:"priority"`
DueDate *time.Time `json:"due_date"`
}
type UpdateTaskRequest struct {
Title *string `json:"title"`
Description *string `json:"description"`
Status *TaskStatus `json:"status"`
Priority *int `json:"priority"`
DueDate *time.Time `json:"due_date"`
}
func (r *CreateTaskRequest) Validate() map[string]string {
errors := make(map[string]string)
if r.Title == "" {
errors["title"] = "Title is required"
}
if len(r.Title) > 255 {
errors["title"] = "Title must be 255 characters or less"
}
if r.Priority < 0 || r.Priority > 5 {
errors["priority"] = "Priority must be between 0 and 5"
}
return errors
}Remarquez comment nous utilisons gorm.DeletedAt pour la suppression douce — les enregistrements ne seront pas définitivement supprimés de la base de données, ce qui facilite leur restauration ultérieure.
Étape 4 : Connexion à la base de données
Configurez la couche base de données dans internal/database/database.go :
package database
import (
"fmt"
"log"
"github.com/yourusername/task-api/internal/config"
"github.com/yourusername/task-api/internal/models"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var DB *gorm.DB
func Connect(cfg *config.Config) {
dsn := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
cfg.DBHost, cfg.DBPort, cfg.DBUser, cfg.DBPassword, cfg.DBName,
)
var err error
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
log.Println("Database connected successfully")
err = DB.AutoMigrate(&models.Task{})
if err != nil {
log.Fatalf("Failed to migrate database: %v", err)
}
log.Println("Database migrated successfully")
}Créez la base de données PostgreSQL avant de lancer l'application :
createdb taskapi
# Ou avec psql :
# psql -U postgres -c "CREATE DATABASE taskapi;"Étape 5 : Construire les handlers de l'API
C'est le cœur de votre API. Créez internal/handlers/task.go :
package handlers
import (
"strconv"
"github.com/gofiber/fiber/v2"
"github.com/yourusername/task-api/internal/database"
"github.com/yourusername/task-api/internal/models"
)
// ListTasks retourne toutes les tâches avec filtrage optionnel
func ListTasks(c *fiber.Ctx) error {
var tasks []models.Task
query := database.DB
// Filtrer par statut si fourni
if status := c.Query("status"); status != "" {
query = query.Where("status = ?", status)
}
// Filtrer par priorité si fourni
if priority := c.Query("priority"); priority != "" {
query = query.Where("priority = ?", priority)
}
// Pagination
page, _ := strconv.Atoi(c.Query("page", "1"))
limit, _ := strconv.Atoi(c.Query("limit", "10"))
offset := (page - 1) * limit
var total int64
query.Model(&models.Task{}).Count(&total)
result := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&tasks)
if result.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Failed to fetch tasks",
})
}
return c.JSON(fiber.Map{
"data": tasks,
"total": total,
"page": page,
"limit": limit,
})
}
// GetTask retourne une seule tâche par ID
func GetTask(c *fiber.Ctx) error {
id := c.Params("id")
var task models.Task
result := database.DB.First(&task, id)
if result.Error != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "Task not found",
})
}
return c.JSON(task)
}
// CreateTask crée une nouvelle tâche
func CreateTask(c *fiber.Ctx) error {
req := new(models.CreateTaskRequest)
if err := c.BodyParser(req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request body",
})
}
// Validation
if errors := req.Validate(); len(errors) > 0 {
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
"errors": errors,
})
}
task := models.Task{
Title: req.Title,
Description: req.Description,
Priority: req.Priority,
DueDate: req.DueDate,
Status: models.StatusPending,
}
result := database.DB.Create(&task)
if result.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Failed to create task",
})
}
return c.Status(fiber.StatusCreated).JSON(task)
}
// UpdateTask met à jour une tâche existante
func UpdateTask(c *fiber.Ctx) error {
id := c.Params("id")
var task models.Task
if err := database.DB.First(&task, id).Error; err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "Task not found",
})
}
req := new(models.UpdateTaskRequest)
if err := c.BodyParser(req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request body",
})
}
// Appliquer les mises à jour partielles
if req.Title != nil {
task.Title = *req.Title
}
if req.Description != nil {
task.Description = *req.Description
}
if req.Status != nil {
task.Status = *req.Status
}
if req.Priority != nil {
task.Priority = *req.Priority
}
if req.DueDate != nil {
task.DueDate = req.DueDate
}
database.DB.Save(&task)
return c.JSON(task)
}
// DeleteTask supprime une tâche (suppression douce)
func DeleteTask(c *fiber.Ctx) error {
id := c.Params("id")
var task models.Task
if err := database.DB.First(&task, id).Error; err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "Task not found",
})
}
database.DB.Delete(&task)
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"message": "Task deleted successfully",
})
}Étape 6 : Ajouter les middlewares
Créez internal/middleware/middleware.go pour la journalisation, CORS et la limitation de débit :
package middleware
import (
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/limiter"
)
func SetupMiddleware(app *fiber.App) {
// CORS
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowMethods: "GET,POST,PUT,PATCH,DELETE",
AllowHeaders: "Origin,Content-Type,Accept,Authorization",
}))
// Limitation de débit : 100 requêtes par minute
app.Use(limiter.New(limiter.Config{
Max: 100,
Expiration: 1 * time.Minute,
LimiterMiddleware: limiter.SlidingWindow{},
}))
// Journalisation des requêtes
app.Use(func(c *fiber.Ctx) error {
start := time.Now()
err := c.Next()
duration := time.Since(start)
log.Printf("%s %s %d %s",
c.Method(),
c.Path(),
c.Response().StatusCode(),
duration,
)
return err
})
}Étape 7 : Assembler le tout
Créez le point d'entrée dans cmd/api/main.go :
package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/gofiber/fiber/v2"
"github.com/yourusername/task-api/internal/config"
"github.com/yourusername/task-api/internal/database"
"github.com/yourusername/task-api/internal/handlers"
"github.com/yourusername/task-api/internal/middleware"
)
func main() {
// Charger la configuration
cfg := config.LoadConfig()
// Se connecter à la base de données
database.Connect(cfg)
// Créer l'application Fiber
app := fiber.New(fiber.Config{
AppName: "Task API v1.0",
ErrorHandler: customErrorHandler,
})
// Configurer les middlewares
middleware.SetupMiddleware(app)
// Vérification de santé
app.Get("/health", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"status": "ok",
"service": "task-api",
})
})
// Routes API v1
v1 := app.Group("/api/v1")
{
tasks := v1.Group("/tasks")
tasks.Get("/", handlers.ListTasks)
tasks.Get("/:id", handlers.GetTask)
tasks.Post("/", handlers.CreateTask)
tasks.Put("/:id", handlers.UpdateTask)
tasks.Delete("/:id", handlers.DeleteTask)
}
// Arrêt gracieux
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-quit
log.Println("Shutting down server...")
if err := app.Shutdown(); err != nil {
log.Fatalf("Server shutdown failed: %v", err)
}
}()
// Démarrer le serveur
addr := fmt.Sprintf(":%s", cfg.AppPort)
log.Printf("Server starting on %s", addr)
log.Fatal(app.Listen(addr))
}
func customErrorHandler(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
return c.Status(code).JSON(fiber.Map{
"error": err.Error(),
})
}Étape 8 : Lancer et tester votre API
Démarrez le serveur :
go run cmd/api/main.goVous devriez voir :
Database connected successfully
Database migrated successfully
Server starting on :3000
Testez maintenant avec curl :
Créer une tâche :
curl -X POST http://localhost:3000/api/v1/tasks \
-H "Content-Type: application/json" \
-d '{
"title": "Apprendre Go Fiber",
"description": "Terminer le tutoriel REST API",
"priority": 3
}'Réponse :
{
"id": 1,
"title": "Apprendre Go Fiber",
"description": "Terminer le tutoriel REST API",
"status": "pending",
"priority": 3,
"created_at": "2026-03-02T10:00:00Z",
"updated_at": "2026-03-02T10:00:00Z"
}Lister toutes les tâches :
curl http://localhost:3000/api/v1/tasksMettre à jour une tâche :
curl -X PUT http://localhost:3000/api/v1/tasks/1 \
-H "Content-Type: application/json" \
-d '{"status": "in_progress"}'Filtrer par statut :
curl "http://localhost:3000/api/v1/tasks?status=pending&page=1&limit=5"Supprimer une tâche :
curl -X DELETE http://localhost:3000/api/v1/tasks/1Étape 9 : Écrire les tests d'intégration
Créez cmd/api/main_test.go :
package main
import (
"bytes"
"encoding/json"
"net/http/httptest"
"testing"
"github.com/gofiber/fiber/v2"
"github.com/yourusername/task-api/internal/handlers"
"github.com/yourusername/task-api/internal/models"
)
func setupTestApp() *fiber.App {
app := fiber.New()
v1 := app.Group("/api/v1")
tasks := v1.Group("/tasks")
tasks.Get("/", handlers.ListTasks)
tasks.Get("/:id", handlers.GetTask)
tasks.Post("/", handlers.CreateTask)
tasks.Put("/:id", handlers.UpdateTask)
tasks.Delete("/:id", handlers.DeleteTask)
return app
}
func TestCreateTask(t *testing.T) {
app := setupTestApp()
task := models.CreateTaskRequest{
Title: "Test Task",
Description: "A task for testing",
Priority: 2,
}
body, _ := json.Marshal(task)
req := httptest.NewRequest("POST", "/api/v1/tasks", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req, -1)
if err != nil {
t.Fatalf("Failed to make request: %v", err)
}
if resp.StatusCode != fiber.StatusCreated {
t.Errorf("Expected status 201, got %d", resp.StatusCode)
}
}
func TestCreateTaskValidation(t *testing.T) {
app := setupTestApp()
task := models.CreateTaskRequest{
Title: "",
}
body, _ := json.Marshal(task)
req := httptest.NewRequest("POST", "/api/v1/tasks", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req, -1)
if err != nil {
t.Fatalf("Failed to make request: %v", err)
}
if resp.StatusCode != fiber.StatusUnprocessableEntity {
t.Errorf("Expected status 422, got %d", resp.StatusCode)
}
}
func TestGetNonExistentTask(t *testing.T) {
app := setupTestApp()
req := httptest.NewRequest("GET", "/api/v1/tasks/99999", nil)
resp, err := app.Test(req, -1)
if err != nil {
t.Fatalf("Failed to make request: %v", err)
}
if resp.StatusCode != fiber.StatusNotFound {
t.Errorf("Expected status 404, got %d", resp.StatusCode)
}
}Lancez les tests :
go test ./cmd/api/ -vDépannage
Erreur "connection refused" lors de la connexion à PostgreSQL :
Assurez-vous que PostgreSQL est en cours d'exécution et que les identifiants dans .env correspondent. Si vous utilisez Docker :
docker run --name taskdb -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:15
docker exec -it taskdb psql -U postgres -c "CREATE DATABASE taskapi;"Erreurs "go: module not found" :
Exécutez go mod tidy pour résoudre les problèmes de dépendances.
Port déjà utilisé :
Changez APP_PORT dans .env ou arrêtez le processus utilisant le port :
lsof -ti:3000 | xargs killComparaison des performances
Voici comment Fiber se compare aux frameworks populaires à travers les langages :
| Framework | Langage | Req/sec (hello world) | Mémoire |
|---|---|---|---|
| Fiber | Go | ~500 000 | ~10 Mo |
| Gin | Go | ~350 000 | ~12 Mo |
| Express | Node.js | ~40 000 | ~80 Mo |
| FastAPI | Python | ~25 000 | ~50 Mo |
| Axum | Rust | ~550 000 | ~8 Mo |
Les benchmarks varient selon le matériel. Source : TechEmpower Framework Benchmarks.
Prochaines étapes
- Ajouter l'authentification JWT avec
github.com/gofiber/jwt/v3 - Ajouter la documentation Swagger avec
github.com/gofiber/swagger - Déployer avec Docker — créer un Dockerfile multi-étapes pour une image minimale
- Ajouter le support WebSocket pour les mises à jour de tâches en temps réel
- Implémenter le cache avec Redis via
github.com/gofiber/storage/redis
Conclusion
Vous avez construit une API REST complète avec Go et Fiber qui comprend :
- Une structure de projet propre suivant les bonnes pratiques de Go
- Des opérations CRUD complètes avec PostgreSQL et GORM
- La validation des entrées et une gestion structurée des erreurs
- Des middlewares pour CORS, la limitation de débit et la journalisation
- Un arrêt gracieux pour la préparation à la production
- Des tests d'intégration utilisant les outils de test intégrés de Fiber
Go et Fiber vous donnent les performances d'un langage compilé avec l'expérience développeur d'Express.js. Le binaire résultant est un exécutable unique et autonome qui démarre en millisecondes et gère des milliers de connexions simultanées avec une consommation mémoire minimale.
Pour votre prochain projet, envisagez d'ajouter l'authentification, le support WebSocket, ou le déploiement sur une plateforme cloud avec Docker. Les fondations que vous avez construites ici s'adaptent des projets personnels aux charges de production servant des millions de requêtes.
Discutez de votre projet avec nous
Nous sommes ici pour vous aider avec vos besoins en développement Web. Planifiez un appel pour discuter de votre projet et comment nous pouvons vous aider.
Trouvons les meilleures solutions pour vos besoins.
Articles connexes

Construire des API REST prêtes pour la production avec FastAPI, PostgreSQL et Docker
Apprenez à créer, tester et déployer une API REST de qualité production en utilisant le framework FastAPI de Python avec PostgreSQL, SQLAlchemy, les migrations Alembic et Docker Compose — de zéro au déploiement.

Construire des API REST avec Rust et Axum : guide pratique pour débutants
Apprenez à construire des API REST rapides et sûres avec Rust et le framework web Axum. Ce guide étape par étape couvre la configuration du projet, le routage, la gestion JSON, l'intégration base de données avec SQLx, la gestion des erreurs et les tests — de zéro à une API fonctionnelle.

Construire une Application Full-Stack avec Drizzle ORM et Next.js 15 : Base de Donnees Type-Safe du Zero a la Production
Apprenez a construire une application full-stack type-safe avec Drizzle ORM et Next.js 15. Ce tutoriel pratique couvre la conception de schemas, les migrations, les Server Actions, les operations CRUD et le deploiement avec PostgreSQL.