Example Microservice with Fiber

Introduction

This is an example project that details the steps needed to create a basic HTTP server using Fiber. It is assumed that the reader has at least a basic understanding of Golang, Microservice architectures and software development practices.

In this example we are going to:

By the end of this you will have a service that listens for an HTTP GET request on a given port and returns the String “Hello World!”. The service will have a number of unit tests and will act at the basis to build out a more complex service. We will also containerise this service with docker allowing it to easily be deployed on any machine. I wouldn’t recommend putting it into production but feel free to disregard that recommendation.

Set up a new project

To start with we need to set up a new project directory

mkdir fiber-example
cd fiber-example

Then we need to initalise the go module, in this case I used the github URL for the project as the module name and created hello.go to act as the main source file for the service.

go mod init github.com/MarkTaylor-KandC/golang-microservices-fiber-example
touch hello.go

Now that we have some basic files that we are working with we should probably version control those with Git

git init

Creating a basic HTTP Server

This is pretty much the same as the example file provided on the Fiber documentation file although adapted more towards the approach used in the unit testing recipe

The main differences between the two are the version for the unit test externalises the set up of fiber instance to allow it to be used by both the main application and the testing framework.

Source

package main

import (
	"log"

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

func main() {
	app := Setup()

	// start the application on http://localhost:3000
	log.Fatal(app.Listen(":3000"))
}

// Setup Setup a fiber app with all of its routes
func Setup() *fiber.App {

	app := fiber.New()

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

	return app
}

In order to use fiber, we are going to need to vendor it:

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

Then we can install and run the service:

go install github.com/MarkTaylor-KandC/golang-microservices-fiber-example
go run hello.go   

The source code for the server is fairly simple and if you are familar with Express this probably all looks very familiar.

  • app is the instance of fiber.
  • Get is the HTTP Request Method that we want to expose.
  • "/" is the path we want to expose the method on.
  • func(c *fiber.Ctx) error is the callback function that is invoked when a request is received and matched to the method and path (route). It contains the context which holds details on the HTTP request and response and allows us to send the response.
  • return c.SendString("Hello World!") sends a string as an HTTP response object and returns the result of that operation.

Containerisation with Docker

As this is a simple application it is easy to create a docker file to containerise the application.

Source

FROM golang:1.18-alpine

WORKDIR /app

COPY go.mod ./
COPY go.sum ./
RUN go mod download

COPY *.go ./

RUN go build -o /golang-microservices-fiber-example

EXPOSE 3000

CMD [ "/golang-microservices-fiber-example" ]

The docker file builds the application and then runs it, exposing the service on port 3000.

We can build it with the following command:

docker build -t golang-microservices-fiber-example .

And run it with:

docker run -p 3000:3000 -t golang-microservices-fiber-example

This will then provide the same result as running above but from within a docker container which should be portable.

Testing with a browser

We can easily test the application using a browser by navigating to http://127.0.0.1:3000/ with either the program running directly or running in the docker container. This is good for a sanity check but we want to also have something that we can run automatically as part of our build process.

Testing with Unit tests

Again this follows the approach set out in the fiber testing recipe.

Source

package main

import (
	"io/ioutil"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestIndexRoute(t *testing.T) {
	
	//Data for each test on the route
	tests := []struct {
		description string
		route string
		expectedError bool
		expectedCode  int
		expectedBody  string
	}{
		{
			description:   "index route",
			route:         "/",
			expectedError: false,
			expectedCode:  200,
			expectedBody:  "Hello World!",
		},
		{
			description:   "non existing route",
			route:         "/i-dont-exist",
			expectedError: false,
			expectedCode:  404,
			expectedBody:  "Cannot GET /i-dont-exist",
		},
	}

	app := Setup()


	for _, test := range tests {
		req, _ := http.NewRequest(
			"GET",
			test.route,
			nil,
		)

		// The -1 disables request latency.
		res, err := app.Test(req, -1)

		assert.Equalf(t, test.expectedError, err != nil, test.description)

		if test.expectedError {
			continue
		}

		// Verify if the status code is as expected
		assert.Equalf(t, test.expectedCode, res.StatusCode, test.description)

		// Read the response body
		body, err := ioutil.ReadAll(res.Body)

		// Ensure that the body was read correctly
		assert.Nilf(t, err, test.description)
		// Verify, that the reponse body equals the expected body
		assert.Equalf(t, test.expectedBody, string(body), test.description)
	}
}

This allows a number of test cases to be set up, at the moment we are only testing that a correct route is handled by our request handler:

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

And that any routes that we don’t account for in the application return a 404 message.

The tests make use of testify library so this will need to be installed

go get -u "github.com/stretchr/testify/assert"

These tests can be run with

go test

These tests can be run with github actions with the default Go settings go.yml

Although including unit tests is useful and good practice, in reality if we follow good Domain Driven Design (DDD) principles I would not expect the controllers to have much logic within them.

Summary

In this tutorial we have put together a basic HTTP based application in Golang using the Fiber framework, although it is only simple it is the groundwork for a more complex go based application, future topics I want to explore:

  • Using all HTTP Methods
  • Adding middleware to run functions on each request
  • Applying 12 factor app principles
  • Authorisation/Authentication
  • Building a simple application with all of the above and database access

GitHub

View Github