HTTP Servers on Fiber in Golang

With a syntax familiar to all Express.js users, Fiber removes unnecessary complexity and lets you focus on what matters most: maximum speed and efficiency.

Basic concepts of Fiber

Asynchrony is the foundation of Fiber. It is built on top of Go routines, which makes it natively asynchronous, allowing it to process multiple requests in parallel with minimal system load. The trick is that Go routines are extremely lightweight — they are faster and require fewer resources than traditional threads in other languages.

Fiber uses goroutine pooling — a technique in which a set of Go routines is prepared in advance to handle requests.

An example of simple asynchronous request processing:

package main

import (
    "github.com/gofiber/fiber/v2"
    "time"
)

func main() {
    app := fiber.New()

    app.Get("/async", func(c *fiber.Ctx) error {
        go func() {
            time.Sleep(2 * time.Second)
            println("Асинхронная задача завершена")
        }()
        return c.SendString("Запрос принят, задача выполняется в фоне!")
    })

    app.Listen(":3000")
}

Request for route /async returns the result immediately, without waiting for the asynchronous task running in the background to complete.

High performance is achieved due to several factors:

  1. Zero Allocation Routing: Fiber routes requests without any additional allocations, minimizing the overhead of request processing.

  2. Multithreading via Go routines: Thanks to Go's built-in multithreading capabilities, Fiber scales horizontally and can handle thousands of requests in parallel.

Example Fiber configuration for max. performance:

package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/logger"
)

func main() {
    app := fiber.New(fiber.Config{
        Prefork:       true,  // включаем предварительное форкование для увеличения производительности на многоядерных процессорах
        ServerHeader:  "Fiber", // добавляем заголовок для идентификации сервера
        CaseSensitive: true,    // включаем чувствительность к регистру в URL
        StrictRouting: true,    // включаем строгую маршрутизацию
    })

    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Hello, Fiber!")
    })

    app.Listen(":3000")
}

Here Prefork Allows you to create multiple processes.

Overview of Basic Syntax

Routing in Fiber is incredibly simple and fast thanks to its zero allocation architecture. Fiber supports methods GET, POST, PUT, DELETEas well as dynamic routing:

app.Get("/users/:id", func(c *fiber.Ctx) error {
    id := c.Params("id")
    return c.SendString("User ID: " + id)
})

Route with dynamic parameter :id allows you to easily work with URLs and pass data to the handler.

Middleware in Fiber is intuitive to connect and works similarly to Express.js:

app.Use(func(c *fiber.Ctx) error {
    println("Запрос получен")
    return c.Next() // передаем управление дальше
})

It is easy to add middleware to handle authorization, logging or various security features.

Fiber's request processing supports working with request bodies, headers, and files. An example of working with request bodies:

app.Post("/submit", func(c *fiber.Ctx) error {
    data := new(struct {
        Name string `json:"name"`
    })
    if err := c.BodyParser(data); err != nil {
        return err
    }
    return c.JSON(fiber.Map{"message": "Привет, " + data.Name})
})

Here Fiber allows you to parse the request body and send a response in JSON format.

One of the big advantages of Fiber is its compatibility with Go's low-level capabilities. Fiber provides an interface for working with native net/http handlers, which allows it to be combined with existing solutions:

app := fiber.New()

httpHandler := func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Привет из net/http"))
}

app.Get("/legacy", func(c *fiber.Ctx) error {
    httpHandler(c.Context().Response().Writer, c.Context().Request())
    return nil
})

Let's continue analyzing the syntax Fiber — a framework that makes working with HTTP servers in Go intuitive and incredibly productive.

Extracting dynamic parameters from a URL is very easy. For example, when creating a route with a dynamic segment, you can easily get the parameter value:

app.Get("/products/:id", func(c *fiber.Ctx) error {
    id := c.Params("id") // извлечение параметра "id" из URL
    return c.SendString("Product ID: " + id)
})

This way you can pass the product ID directly in the URL and process it in the handler function.

Query parameters that are passed in the query string (e.g. ?sort=desc), are also easily extracted via Fiber:

app.Get("/search", func(c *fiber.Ctx) error {
    query := c.Query("q", "default") // получаем значение параметра "q", задаём "default" как значение по умолчанию
    return c.SendString("Searching for: " + query)
})

If you need to work with request headers, Fiber has a convenient API for extracting them:

app.Get("/headers", func(c *fiber.Ctx) error {
    userAgent := c.Get("User-Agent") // извлекаем заголовок User-Agent
    return c.SendString("Your User-Agent is: " + userAgent)
})

Server example

Now let's create a full-fledged server on Fiber. We will implement an HTTP server for an online cat food store.

Project structure

Let's define the project structure so that it is convenient for development and scaling:

cat-food-store/
│
├── main.go          // Главная точка входа
├── routes/          // Каталог с файлами маршрутов
│   └── products.go  // Маршруты для работы с продуктами (корм)
├── handlers/        // Обработчики для запросов
│   └── product.go   // Логика обработки продуктов
├── models/          // Модели для базы данных
│   └── product.go   // Модель данных для корма
└── database.go      // Подключение к базе данных

Now you can start with the main file main.go.

Implementation of the main file main.go

This file will be responsible for initializing the application, connecting to the database, configuring middleware and starting the server:

package main

import (
	"log"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/fiber/v2/middleware/compress"
	"github.com/gofiber/fiber/v2/middleware/limiter"
	"github.com/gofiber/fiber/v2/middleware/recover"
	"cat-food-store/database"
	"cat-food-store/routes"
)

func main() {
	// инициализируем базу данных
	if err := database.Connect(); err != nil {
		log.Fatalf("Ошибка подключения к базе данных: %v", err)
	}

	// создаём новое приложение Fiber
	app := fiber.New(fiber.Config{
		Prefork: true, // используем предварительное форкование для увеличения производительности
	})

	// Подключаем middleware
	app.Use(logger.New())      // Логирование запросов
	app.Use(compress.New())    // Сжатие ответов
	app.Use(recover.New())     // Восстановление после паники
	app.Use(limiter.New())     // Лимит запросов для предотвращения DDOS атак

	// Регистрация маршрутов
	routes.RegisterProductRoutes(app)

	// Запускаем сервер
	log.Fatal(app.Listen(":3000"))
}

Connecting to the DB

We will use PostgreSQL as a database to store information about products (feed). To connect to the database, we will create a file database.go:

package database

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/lib/pq"
)

var DB *sql.DB

// функция подключения к базе данных
func Connect() error {
	connStr := "user=username dbname=catfoodstore sslmode=disable password=yourpassword"
	db, err := sql.Open("postgres", connStr)
	if err != nil {
		return fmt.Errorf("ошибка подключения к БД: %v", err)
	}

	if err := db.Ping(); err != nil {
		return fmt.Errorf("не удалось подключиться к базе данных: %v", err)
	}

	DB = db
	log.Println("Успешно подключились к базе данных")
	return nil
}

Data model for products (models/product.go)

Now let's create a data model for the product that will store information about cat food:

package models

type Product struct {
	ID          int     `json:"id"`
	Name        string  `json:"name"`
	Description string  `json:"description"`
	Price       float64 `json:"price"`
	Stock       int     `json:"stock"`
	ImageURL    string  `json:"image_url"`
}

Request handler for products (handlers/product.go)

The handler will contain logic for interacting with the database: creating, receiving, updating and deleting products:

package handlers

import (
	"cat-food-store/database"
	"cat-food-store/models"
	"github.com/gofiber/fiber/v2"
	"strconv"
)

// получение списка всех продуктов
func GetProducts(c *fiber.Ctx) error {
	rows, err := database.DB.Query("SELECT id, name, description, price, stock, image_url FROM products")
	if err != nil {
		return c.Status(500).SendString("Ошибка выполнения запроса к базе данных")
	}
	defer rows.Close()

	var products []models.Product
	for rows.Next() {
		var product models.Product
		err := rows.Scan(&product.ID, &product.Name, &product.Description, &product.Price, &product.Stock, &product.ImageURL)
		if err != nil {
			return c.Status(500).SendString("Ошибка сканирования данных")
		}
		products = append(products, product)
	}

	return c.JSON(products)
}

// создание нового продукта
func CreateProduct(c *fiber.Ctx) error {
	product := new(models.Product)
	if err := c.BodyParser(product); err != nil {
		return c.Status(400).SendString("Неверный формат запроса")
	}

	_, err := database.DB.Exec("INSERT INTO products (name, description, price, stock, image_url) VALUES ($1, $2, $3, $4, $5)",
		product.Name, product.Description, product.Price, product.Stock, product.ImageURL)
	if err != nil {
		return c.Status(500).SendString("Ошибка вставки данных в базу")
	}

	return c.Status(201).SendString("Продукт успешно создан")
}

// получение продукта по ID
func GetProduct(c *fiber.Ctx) error {
	id := c.Params("id")
	row := database.DB.QueryRow("SELECT id, name, description, price, stock, image_url FROM products WHERE id = $1", id)

	var product models.Product
	err := row.Scan(&product.ID, &product.Name, &product.Description, &product.Price, &product.Stock, &product.ImageURL)
	if err != nil {
		return c.Status(404).SendString("Продукт не найден")
	}

	return c.JSON(product)
}

// обновление продукта
func UpdateProduct(c *fiber.Ctx) error {
	id := c.Params("id")
	product := new(models.Product)

	if err := c.BodyParser(product); err != nil {
		return c.Status(400).SendString("Неверный формат запроса")
	}

	_, err := database.DB.Exec("UPDATE products SET name = $1, description = $2, price = $3, stock = $4, image_url = $5 WHERE id = $6",
		product.Name, product.Description, product.Price, product.Stock, product.ImageURL, id)
	if err != nil {
		return c.Status(500).SendString("Ошибка обновления данных")
	}

	return c.SendString("Продукт успешно обновлён")
}

// удаление продукта
func DeleteProduct(c *fiber.Ctx) error {
	id := c.Params("id")
	_, err := database.DB.Exec("DELETE FROM products WHERE id = $1", id)
	if err != nil {
		return c.Status(500).SendString("Ошибка удаления продукта")
	}

	return c.SendString("Продукт успешно удалён")
}

Routes for products (routes/products.go)

Now let's register routes for working with products in a separate file:

package routes

import (
	"cat-food-store/handlers"
	"github.com/gofiber/fiber/v2"
)

func RegisterProductRoutes(app *fiber.App) {
	api := app.Group("/api")

	api.Get("/products", handlers.GetProducts)      // Получить все продукты
	api.Post("/products", handlers.CreateProduct)   // Создать новый продукт
	api.Get("/products/:id", handlers.GetProduct)   // Получить продукт по ID
	api.Put("/products/:id", handlers.UpdateProduct) // Обновить продукт
	api.Delete("/products/:id", handlers.DeleteProduct) // Удалить продукт
}

We created a full-fledged server for a cat food store on Fiber, which includes:

  1. Logging requests

  2. Compress responses to improve performance

  3. Limiting requests to prevent attacks

  4. Connecting to a PostgreSQL database

  5. Full routing for working with products (cat food)

  6. CRUD operations


More details about Fiber can be found here read here.

And you can register at the link free webinar course “Golang Developer. Professional”.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *