Backend••13 min read•21 views

Understanding Golang Fiber: From Basic Concepts to Application Architecture

Golang Fiber is a web framework for the Go programming language that is designed to be very fast, light and efficient. Inspired by Express.js in the Node.js ecosystem, Fiber offers intuitive syntax and minimal boilerplate, making it very easy to learn.

Why Choose Golang Fiber?

In the world of backend development, speed, memory efficiency, and developer productivity (developer experience) are the three main pillars that determine the success of a project. The Go (Golang) programming language has long been known as the king of performance thanks to its built-in concurrency (goroutines) and its direct compilation produces a single binary (single binary).

By default, Go provides a very solid net/http package that is used by millions of applications around the world. However, as application complexity increases—such as the need for complicated routing, handling middleware, data validation, and external library management—writing code based on net/http often feels too long (boilerplate code) and time consuming.

This is where Fiber comes in.

Fiber is a web framework for Go designed with major inspiration from Express.js (the most popular framework in the Node.js ecosystem). Fiber provides elegant, pure, minimal syntax boilerplate, without sacrificing the extreme speed that Go is known for.

The following is a diagram of the architecture in a neat and easy-to-read table form:

Layer Main Components Main Roles & Functions
Layer 1: Top Level Your Application The business logic, domain and endpoint handling of the applications you build.
Layer 2: Framework Golang Fiber Provides the Routing, Middleware, Body Parser, and Context Management (fiber.Ctx).
Layer 3: Engine Fasthttp Engine High-speed HTTP with memory optimizationZero Allocation.
Layer 4: Runtime Runtime Go The main foundation of the system that manages Goroutines (concurrency) and Garbage Collector.

The Secret Behind Fiber Performance: Fasthttp Architecture

Many developers ask: "If Go's net/http standard library is already so fast, how can Fiber claim to be so much faster?"

The answer lies in the underlying libraries it uses. Most other popular Go frameworks (such as Gin, Echo, or Chi) are built on top of the net/http standard library. In contrast, Fiber is built on Fasthttp, an alternative HTTP engine for Go that is optimized to the extreme to handle thousands of requests per second (Requests Per Second / RPS) with very low memory usage.

Why Are Fasthttp and Fiber So Fast?

  1. Zero Memory Allocation:

    In standard net/http, every time an HTTP request comes in, the Go runtime allocates a new object in memoryheap. The more requests, the more memory allocations occur, so Go's Garbage Collector (GC) has to work extra hard to clean up that memory. Fasthttp and Fiber use the object pooling technique (sync.Pool). Instead of creating a new object for each request, Fiber recycles request and response. As a result, memory spikes can be reduced to a minimum.

  2. Reduced String-to-Byte Conversion Overhead:

    In Go, conversion between string and []byte usually requires a new memory allocation due to the nature of string which is immutable (cannot be changed). Fiber leverages Fasthttp's internal memory allocation to avoid re-allocation when reading raw data from TCP sockets.

  3. Input/Output Buffer Optimization:

    The data processing process stream from the client is optimized using high-performance buffering techniques, so that IO read and write operations do not burden the operating system.

Important Note:

Because Fiber uses Fasthttp, Fiber is not directly compatible with http.Handler or http.Request built into the Go standard library (net/http). However, Fiber provides an internal adapter if you really need to connect a net/http based library.

Work Environment Preparation and Installation

To start using Fiber, you need the Go programming environment installed on your computer (Go version 1.18 or later is recommended).

Step 1: Initialize Go Project

Create a new directory for your project, then run the command go mod init in the terminal:

Bash
mkdir learn-golang-fiber
cd learn-golang-fiber
go mod init learn-golang-fiber

Step 2: Install the Fiber Framework

Install the Fiber v2 package (the current main stable version) using the command go get:

Bash
go get -u github.com/gofiber/fiber/v2

Understanding the Basic Structure and Anatomy of Fiber Code

Let's dissect the most basic code anatomy in creating Fiber-based web applications. Create a file named main.go.

Go
package main

import (
	"log"

	"github.com/gofiber/fiber/v2"
)

func main() {
	// 1. Create a new Fiber application instance
	app := fiber.New(fiber.Config{
		Prefork: false, // Change to true if you want to take full advantage of the multi-core architecture
		CaseSensitive: true, // Differentiate between /user and /User URLs
		StrictRouting: true, // Differentiate between /user/ and /user URLs
		AppName: "My Fiber Application v1.0",
	})

	// 2. Basic Route Definition
	app.Get("/", func(c *fiber.Ctx) error {
		return c.SendString("Welcome to the World of Golang Fiber!")
	})

	// 3. Running the Server on Port 3000
	log.Fatal(app.Listen(":3000"))
}

Main Component Code Explanation:

  • fiber.New(config...): This function initializes the Fiber application instantiation. You can enter optional configurations such as choosing an application name, turning on the Prefork feature, customizing the upload limit size, and specifying error handling (error handler).

  • fiber.Ctx (Context): This is the most vital component in Fiber. A c object of type *fiber.Ctx represents the current HTTP context. All data related to incoming requests (request) and outgoing responses (response) is managed via the This Ctx.

  • app.Listen(":3000"): Starts the TCP server and listens for incoming HTTP requests on port 3000.

Important Concepts: Routing & Parameter Handling

Routing is the process of mapping URL addresses requested by users to specific handler functions (handler) on the server side. Fiber provides a very intuitive and flexible routing API.

1. HTTP Methods (GET, POST, PUT, DELETE, PATCH)

Fiber supports all HTTP standards:

Go
app.Get("/api/products", getProductsHandler)
app.Post("/api/products", createProductHandler)
app.Put("/api/products/:id", updateProductHandler)
app.Delete("/api/products/:id", deleteProductHandler)
app.Patch("/api/products/:id", partialUpdateProductHandler)

2. Handling URL Parameters (Path Parameters)

To retrieve dynamic values from a URL, use a colon (:). You can retrieve the value using the c.Params() method.

Go
// Endpoint: GET /users/1024
app.Get("/users/:id", func(c *fiber.Ctx) error {
	userId := c.Params("id")
	return c.SendString("Display user data with ID: " + userId)
})

// Optional Parameters (question mark ?)
// Endpoint: GET /flights/JAKARTA-BALI or /flights/
app.Get("/flights/:route?", func(c *fiber.Ctx) error {
	if c.Params("route") != "" {
		return c.SendString("Flight route: " + c.Params("route"))
	}
	return c.SendString("Displays the entire flight route")
})

// Parameter Wildcard (asterisk *)
// Endpoint: GET /files/documents/pdf/report.pdf
app.Get("/files/*", func(c *fiber.Ctx) error {
	filePath := c.Params("*")
	return c.SendString("Open file in path: " + filePath)
})

3. Handling Query Parameters

Query parameters are data sent via the URL after the question mark ?. Example: /search?keyword=golang&page=2. To read it, use c.Query().

Go
app.Get("/search", func(c *fiber.Ctx) error {
	keyword := c.Query("keyword", "default_keyword") // "default_keyword" if not specified
	page := c.Query("page", "1")

	return c.JSON(fiber.Map{
		"keyword": keyword,
		"page": page,
	})
})

4. Route Grouping

As your app gets bigger, you'll want to separate URLs by version or feature domain (for example /api/v1/ and /api/v2/). Fiber provides a Group feature for neat code architecture.

Go
func setupRoutes(app *fiber.App) {
	// Group API v1
	v1 := app.Group("/api/v1")

	v1.Get("/users", getAllUsers)
	v1.Post("/users", createUser)

	// Sub-group for Product Module
	products := v1.Group("/products")
	products.Get("/", getAllProducts)
	products.Get("/:id", getProductById)
}

Request Body Processing and Data Validation

One of the main tasks of the backend is to receive data from the client (for example JSON format from a React, Vue, or Flutter application), validate it, and process it.

1. Parsing Request Body (JSON, Form Data, XML)

Fiber provides a powerful method called c.BodyParser(). This method automatically detects Header Content-Type from incoming request and performs mapping (unmarshaling) data into the variable struct Go.

Go
type CreateUserRequest struct {
	Name string `json:"name" form:"name"`
	Email string `json:"email" form:"email"`
	Password string `json:"password" form:"password"`
}

app.Post("/api/users", func(c *fiber.Ctx) error {
	// Struct instantiation
	req := new(CreateUserRequest)

	// Parse body to struct
	if err := c.BodyParser(req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": true,
			"message": "Invalid request body format",
		})
	}

	// Process business logic (e.g. save to database)...

	return c.Status(fiber.StatusCreated).JSON(fiber.Map{
		"error": false,
		"message": "User successfully created",
		"data": req,
	})
})

2. Input Data Validation

 

Go
package main

import (
	"github.com/go-playground/validator/v10"
	"github.com/gofiber/fiber/v2"
)

type RegisterDTO struct {
	Username string `json:"username" validate:"required,min=3,max=20"`
	Email string `json:"email" validate:"required,email"`
	Age int `json:"age" validate:"gte=18,lte=60"`
}

var validate = validator.New()

func registerHandler(c *fiber.Ctx) error {
	dto := new(RegisterDTO)

	if err := c.BodyParser(dto); err != nil {
		return c.Status(fiber.StatusBadRequest).SendString(err.Error())
	}

	// Run Validation
	if err := validate.Struct(dto); err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
			"message": "Validation failed",
			"details": err.Error(),
		})
	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message": "Registration successful!",
	})
}

Understanding and Implementing Middleware

Middleware is a function that is executed before or after the main handler function (route handler) is executed. Middleware is in the middle of the request-response flow and is typically used to:

  • Logging request history (Logging)

  • Secure routes from unauthorized access (Authentication & Authorization)

  • Prevent attacks Cross-Origin (CORS)

  • Quota limiting request (Rate Limiting)

  • Handling Recovery from panic so that the server does not shut down.

Request ---> [ Middleware 1 ] ---> [ Middleware 2 ] ---> [ Route Handler ]
                                                                 |
Response <--- [ Middleware 1 ] <--- [ Middleware 2 ] <-----+

1. Using Fiber's Built-in Middleware

Fiber provides dozens of ready-to-use official middlewares in the [github.com/gofiber/fiber/v2/middleware/](https://github.com/gofiber/fiber/v2/middleware/)....

Go
package main

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

func setupMiddleware(app *fiber.App) {
	// 1. Logger: Logs every request in the terminal
	app.Use(logger.New(logger.Config{
		Format: "[${time}] ${status} - ${latency} ${method} ${path}\n",
	}))

	// 2. Recover: Prevents server crashes if a panic occurs in the code
	app.Use(recover.New())

	// 3. CORS: Allow access from specific Frontend domains
	app.Use(cors.New(cors.Config{
		AllowOrigins: "https://myfrontend.com, https://localhost:3000",
		AllowHeaders: "Origin, Content-Type, Accept, Authorization",
		AllowMethods: "GET, POST, PUT, DELETE",
	}))
}

2. Creating Custom Middleware (Custom Middleware)

You can create your own middleware very easily. Just return the function value func(c *fiber.Ctx) error. To continue execution to the next handler, call c.Next().

Go
// Middleware to check the Api-Key Header
func ApiKeyMiddleware() fiber.Handler {
	return func(c *fiber.Ctx) error {
		apiKey := c.Get("X-API-KEY")

		if apiKey != "SUPER-123-SECRET" {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error": "Access Denied: Invalid API Key",
			})
		}

		// Continue to the next Middleware or Handler
		return c.Next()
	}
}

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

	// Deploy custom middleware on protected routes
	api := app.Group("/api", ApiKeyMiddleware())

	api.Get("/dashboard", func(c *fiber.Ctx) error {
		return c.SendString("Congratulations, you have successfully accessed the protected dashboard!")
	})

	app.Listen(":3000")
}

Error Handling

In production-scale systems, consistent error handling is fundamental. Fiber has a centralized Error Handler mechanism that can be completely customized during initial application initialization.

Go
package main

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

func main() {
	app := fiber.New(fiber.Config{
		// Custom Centralized Error Handler
		ErrorHandler: func(c *fiber.Ctx, err error) error {
			// Default status code is 500 (Internal Server Error)
			code := fiber.StatusInternalServerError

			// Check whether the error is of type *fiber.Error
			var e *fiber.Error
			if errors.As(err, &e) {
				code = e.Code
			}

			// Returns standard JSON Error format for the entire application
			return c.Status(code).JSON(fiber.Map{
				"success": false,
				"message": err.Error(),
				"code": code,
			})
		},
	})

	app.Get("/trigger-error", func(c *fiber.Ctx) error {
		// Returns a specific error 404
		return fiber.NewError(fiber.StatusNotFound, "Resource not found!")
	})

	app.Listen(":3000")
}

Database Connection and Configuration Management (GORM Integration)

Modern web applications almost always require integration with a database (database). The most common ORM library paired with Fiber in the Go ecosystem is GORM.

The following is an overview of the Fiber integration architecture with PostgreSQL Database using GORM:

Go
package main

import (
	"fmt"
	"log"

	"github.com/gofiber/fiber/v2"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

// Database Model
type Product struct {
	gorm.Model
	Code string `json:"code" gorm:"unique;not null"`
	Price uint `json:"price"`
}

// Global DB Instance (In real projects, use Dependency Injection)
var DB *gorm.DB

func initDatabase() {
	dsn := "host=localhost user=postgres password=root dbname=fiber_db port=5432 sslmode=disable"
	var err error
	DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
	}

	fmt.Println("Database Connection Successful!")

	// Auto Migration Model
	DB.AutoMigrate(&Product{})
}

func main() {
	initDatabase()

	app := fiber.New()

	// Add Product Endpoint
	app.Post("/products", func(c *fiber.Ctx) error {
		product := new(Product)
		if err := c.BodyParser(product); err != nil {
			return c.Status(400).SendString(err.Error())
		}

		result := DB.Create(&product)
		if result.Error != nil {
			return c.Status(500).SendString("Failed to save to database")
		}

		return c.Status(201).JSON(product)
	})

	// Endpoint Retrieves Entire Product
	app.Get("/products", func(c *fiber.Ctx) error {
		var products []Product
		DB.Find(&products)
		return c.JSON(products)
	})

	app.Listen(":3000")
}

Large Scale Project Structure (Clean Architecture / Layered)

When building a real project (production-grade), putting all the code in a single main.go file is a maintenance disaster. We need to implement separation of responsibilities (Separation of Concerns).

The following is a recommended Go Fiber application directory structure based on the pattern Layered Architecture:

Plaintext
my-fiber-app/
├── config/ # Environment, Database & App Configuration Settings
│ ├── config.go
│ └── database.go
├── controllers/ # HTTP Handler Layer (Receive Request & Return Response)
│ ├── auth_controller.go
│ └── user_controller.go
├── models/ # Schema Database (GORM Structs)
│ └── user.go
├── repository/ # Data Access Layer (Direct Database Query)
│ └── user_repository.go
├── services/ # Business Logic Layer
│ └── user_service.go
├── middleware/ # Custom Middlewares (JWT, Auth, CORS)
│ └── jwt_middleware.go
├── routes/ # URL Route Mapping
│ └── routes.go
├── .env # Environment Variables
├── go. mod
├── go.sum
└── main.go # Application Main Entrypoint

Quick Case Study: Implementation of JWT (JSON Web Token) Authentication

Here is a summary of the scenario of how JWT token based authentication is implemented in Fiber:

Go
package main

import (
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/golang-jwt/jwt/v5"
)

const SecretKey = "SecretKeyVerySafeSuperVery"

// Handler Login to generate JWT Token
func loginHandler(c *fiber.Ctx) error {
	// Example of dummy identity validation...
	username := c.FormValue("username")
	password := c.FormValue("password")

	if username != "admin" || password != "secret" {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
			"message": "Invalid credentials",
		})
	}

	// Creating Payload Claims
	claims := jwt.MapClaims{
		"username": username,
		"admin": true,
		"exp": time.Now().Add(time.Hour * 72).Unix(), // Token expires in 72 hours
	}

	// Create a Token with the HMAC SHA256 algorithm
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	t, err := token.SignedString([]byte(SecretKey))
	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	return c.JSON(fiber.Map{
		"token": t,
	})
}

// Custom Middleware to Verify JWT
func JWTMiddleware() fiber.Handler {
	return func(c *fiber.Ctx) error {
		authHeader := c.Get("Authorization")
		if authHeader == "" || len(authHeader) <8 || authHeader[:7] != "Bearer " {
			return c.Status(401).JSON(fiber.Map{"message": "Authorization header not found or incorrect format"})
		}

		tokenString := authHeader[7:]

		// Token Parsing and Validation
		token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			return []byte(SecretKey), nil
		})

		if err != nil || !token.Valid {
			return c.Status(401).JSON(fiber.Map{"message": "Token is invalid or has expired"})
		}

		// Saves JWT claims data into Context Fiber for use in the next handler
		claims := token.Claims.(jwt.MapClaims)
		c.Locals("user_data", claims)

		return c.Next()
	}
}

Comparison: Fiber vs Other Golang Frameworks

To help you choose the right tool, here is an objective comparison table between Fiber, Gin, and Echo:

Features / Criteria Golang Fiber Gin Gonic Echo
Basic Engine Fasthttp net/http default net/http default
Syntax Inspiration Express.js (Node) Custom / Clean Custom / Clean
Speed & RPS Very High (Top Tier) Height Height
Memory Usage Very Low (Zero Alloc) Low Low
Standard Compatibility net/http Requires Adapter Native / Default Native / Default
Built-in Features Overflow (Built-in Batteried) Enough Enough
WebSocket Support Available via Official Package Need External Library Available


When to Use Fiber (and When Not)?

✅ Highly Recommended Using Fiber If:

  1. Developing High Density Microservices: Systems that must process tens of thousands of requests per second with very strict RAM consumption limits (e.g. Cloud Server / Kubernetes infrastructure).

  2. Team Moving Out of the Node.js/Express Ecosystem: Curve learning (learning curve) teams will be very fast due to the similarity of concepts ctx, middleware, and routing.

  3. Building a REST API / GraphQL API: Fiber has all the tools needed for efficient JSON processing.

❌ Avoid / Reconsider Using Fiber If:

  1. Highly Dependent on Ecosystem Middleware net/http Standard: If your application requires high engagement with legacy Go libraries that only accept http.HandlerFunc, adapting it to Fasthttp/Fiber may add complexity.

  2. Requires HTTP/2 or HTTP/3 Support Natively: Fasthttp has some limitations regarding its implementation of certain complex HTTP/2 protocols when compared to the standard package net/http Go updated live by the Go core team.

Conclusion

Golang Fiber succeeds in breaking the web programming paradigm in Go by combining the two best advantages: Flexible syntax like Express.js and Fasthttp-based extreme performance speed.

With the Zero Memory Allocation architecture, routing is based on a tree efficient, and rich built-in middleware ecosystem, Fiber is a mature, mainstream choice for building modern web applications, RESTful APIs, and even high-load Microservices systems (high-concurrency).

Sigit Wasis Subekti

Sigit Wasis Subekti

Software Engineer & Tech Educator

Software Engineer and Tech Educator sharing insights on web development and software architecture.