# Building and Validating Web Endpoints with Fiber in Golang

Fiber, a web framework inspired by Express and built atop Fasthttp, the quickest HTTP engine for Go, is designed to simplify rapid development while keeping zero memory allocation and performance as key considerations.

In this blog post, we will learn how to create endpoints and middleware and validate requests with Fiber.

If you would rather learn via watching a video, here is the YouTube version of this post.

%[https://www.youtube.com/watch?v=dtgrKy-cjFU&t=1026s&pp=ygULbWZrIGluIHRlY2g%3D] 

To start with Golang, first, let’s create an empty project with the `go.mod` file.

```jsx
module fiber

go 1.21
```

Install the Fiber library:

```jsx
go get github.com/gofiber/fiber/v2
```

**Creating a Simple GET Endpoint**

Start by creating a `main.go` file.

```jsx
package main

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

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

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

    app.Listen(":3000")
}
```

It is like a minimal API in dotnet.

* `app` is an instance of Fiber
    
* `Method` is an HTTP request method: `GET`, `PUT`, `POST`, etc.
    
* `path` is a virtual path on the server
    
* `func(*fiber.Ctx) error` is a callback function containing the `Context` executed when the route is matched
    

Now, let's compile our project.

```jsx
go build main.go
```

**Add Basic GET Routing**

Each route can have **multiple handler functions** that are executed when the route is matched.

```jsx
  app.Get("/orders/code/:orderCode", func(c *fiber.Ctx) error {
		return c.SendString("order code that you sent is: " + c.Params("orderCode"))
	})

	app.Get("/users/name/:name?", func(c *fiber.Ctx) error {
		if c.Params("name") != "" {
			return c.SendString("Hello " + c.Params("name"))
		}
		return c.SendString("Where is mfk?")
	})
```

**Add POST Routing**

Here's how to handle POST requests:

```jsx
app.Post("/orders", func(ctx *fiber.Ctx) error {
		var request CreateOrderRequest

		err := ctx.BodyParser(&request)
		if err != nil {
			return err
		}

		return ctx.Status(nethttp.StatusCreated).JSON("Order created successfully")
	})

type CreateOrderRequest struct {
	ShipmentNumber string `json:"shipmentNumber"`
}
```

```jsx
curl --header "Content-Type: application/json" \\
  --request POST \\
  --data '{"shipmentNumber": "55555"}' \\
  <http://localhost:3000/orders>
```

**Middleware**

Middleware functions can perform actions like logging or authentication.

You need to define your middleware before registering endpoints, or it won’t work.

**Creating the First Middleware**

This middleware logs every request when called.

```jsx
app.Use(func(c *fiber.Ctx) error {
		fmt.Println("Called " + string(c.Request().RequestURI()))

		return c.Next()
	})
```

**Adding CorrelationId Middleware**

* Example: Correlation Id middleware. This middleware gets `correlation-id` from the header and puts into Locals key-value, so you can access it from Context, if you want to.
    

```jsx
app.Use("/orders/code/:orderCode", func(c *fiber.Ctx) error {
        // middleware logic
		var correlationId = c.Get("x-correlationid")

		if correlationId == "" {
			return c.Status(nethttp.StatusBadRequest).JSON("x-correlationid is mandatory")
		}

		_, err := uuid.Parse(correlationId)
		if err != nil {
			return c.Status(nethttp.StatusBadRequest).JSON("x-correlationid must be guid")
		}

		c.Locals("correlationId", correlationId)
		return c.Next()
	})

app.Get("/orders/code/:orderCode", func(c *fiber.Ctx) error {
        // Updated GET route to demonstrate middleware usage
        fmt.Printf("Your correlationId is %v", ctx.Locals("correlationId")) // Added this line to the function

		return c.SendString("order code that you sent is: " + c.Params("orderCode"))
	})
```

```jsx
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -H "x-correlationid: a4ebf613-4bc2-4ceb-8046-4042e31f333b" -X GET <http://localhost:3000/orders/code/mfkcode>
```

**Recover Middleware**

Use Fiber's `recover` middleware for error handling. With this middleware, when you panic inside of an endpoint, the app is not going to crash.

```jsx
"github.com/gofiber/fiber/v2/middleware/recover"

app.Use(recover.New())

app.Get("/", func(ctx *fiber.Ctx) error {
		panic("Panic at the disco!")
		return ctx.SendString("mfk test")
	})
```

**Error Handler Middleware**

Customize error responses with an error handler. You can return customized Base Error Responses.

```jsx
app := fiber.New(fiber.Config{
		ErrorHandler: func(c *fiber.Ctx, err error) error {
			code := fiber.StatusInternalServerError

			var e *fiber.Error
			if errors.As(err, &e) {
				code = e.Code
			}

			c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)

			return c.Status(code).SendString("something happened at the disco!")
		},
	})
```

* There are a lot of written middleware in fiber. Check out the [docs](https://docs.gofiber.io/category/-middleware).
    

**Validation**

The last section in fiber is about request validation. In the real world, we always need to validate requests and return some actionable and structured custom error messages.

Validator library is de facto for this job, and it works with fiber.

First, let’s install the library.

```jsx
go get github.com/go-playground/validator/v10
```

Now, let's define validation structures and custom validators. Then, Implement validation in our routes.

```jsx
// You can write this section above main function
type ValidationError struct {
	HasError bool
	Field    string
	Tag      string
	Value    interface{}
}

type CustomValidator struct {
	validator *validator.Validate
}

var validate = validator.New()

func (v CustomValidator) Validate(data interface{}) []ValidationError {
	var validationErrors []ValidationError

	errs := validate.Struct(data)
	if errs != nil {
		for _, err := range errs.(validator.ValidationErrors) {
			var ve ValidationError

			ve.Field = err.Field()
			ve.Tag = err.Tag()
			ve.Value = err.Value()
			ve.HasError = true

			validationErrors = append(validationErrors, ve)
		}
	}

	return validationErrors
}
```

```jsx
// You can write this section in the beginning of main function
customValidator := &CustomValidator{
		validator: validate,
	}
```

Now, let's add validation tags to `CreateOrderRequest`.

`required` means not null, `len` means length.

```jsx
type CreateOrderRequest struct {
	ShipmentNumber string `json:"shipmentNumber" validate:"required"`
    // New Property
	CountryCode    string `json:"countryCode" validate:"required,len=2"`
}
```

Now we can validate the request in the Create Order POST endpoint.

```jsx
if errs := customValidator.Validate(request); len(errs) > 0 && errs[0].HasError {
			errorMessages := make([]string, 0)

			for _, err2 := range errs {
				errorMessages = append(errorMessages, fmt.Sprintf("%s field failed. Validation: '%s'", err2.Field, err2.Tag))
			}

			return ctx.Status(nethttp.StatusBadRequest).JSON(strings.Join(errorMessages, " and that "))
		}
```

There are a lot of validate functions, but if you have custom logic, you can create your own validation tag as well.

```jsx
// You can write this section after creating customValidator object.
customValidator.validator.RegisterValidation("oldAge", func(fl validator.FieldLevel) bool {
		return fl.Field().Int() < 40
	})
```

Let's try our new `oldAge` validation tag. First, add a property to the CreateOrderRequest struct.

```jsx
type CreateOrderRequest struct {
	ShipmentNumber string `json:"shipmentNumber" validate:"required"`
	CountryCode    string `json:"countryCode" validate:"required,len=2"`
    // New Property
	Age            int `json:"age" validate:"required,oldAge"`
}
```

Now, we can call our endpoint. First, without the age property, so we can see the error message.

```jsx
curl --header "Content-Type: application/json" \\
  --request POST \\
  --data '{"shipmentNumber": "55555", "countryCode": "TR"}' \\
  <http://localhost:3000/orders>
```

Response is:

*"Age field has failed. Validation is: required"*

Now, with the `age` property.

```jsx
curl --header "Content-Type: application/json" \\
  --request POST \\
  --data '{"shipmentNumber": "55555", "countryCode": "TR", "age": 45}' \\
  <http://localhost:3000/orders>
```

This is how you can create endpoints and middleware and validate requests with Fiber. If you want to get the full source code, [here](https://github.com/MehmetFiratKomurcu/Youtube-Channel/tree/master/golang-fiber) it is.

May the force be with you!
