An Introduction to Fiber, an Alternative to Express.js
By: Louis Petrik
Tags: Nodejs, JavaScript, Golang, Web Development, Expressjs

An Introduction to Fiber, an Alternative to Express.js

Golang’s performant Express.js alternative in comparison

Source: The author

Golang is trending. And with the rise of a programming language, libraries and frameworks for it are coming out. For Google’s Golang, there is even a web framework, which is very similar to Express.js — but it has the claim to be a better performing alternative.

In this article, we will look at the basics of Fiber and how similar it is to Express.js. We will also do a performance comparison and see which framework is faster.

You don’t need a lot of knowledge about Golang for this article — if you still want to take a little Golang-crash course, here is one.

Let’s Go

First, you need to install Golang; you can do it here https://golang.org/.

In your terminal, you can simply enter go version to test if the installation worked. Now we can already start to install Fiber.

To do this, start by creating a new project using the Go-CLI. It is a bit like the node world where we start with npm init. So we create a package.json for our project. In Go, this file is the go.mod.

go mod init

After that, we can install the Fiber framework for our project. We do this with the following command, which works like npm install.

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

Now we can implement a hello world with Fiber. For this, we create a main.go in the same folder as our go.mod already has. Here we write all our code.

package main import "github.com/gofiber/fiber/v2" func main() {

app := fiber.New() app.Get("/", func(c *fiber.Ctx) error {

return c.SendString("Hello, World :)")

}) app.Listen(":8080")

}

With go run main.go we can run the main.go.

If you now open localhost:8080 in your browser, you should see our first page.

Here we can already see how similar it is to Express.js.

In Golang, we always have to define a package; under it, we import the Fiber framework with Express.

Unlike JavaScript, Go depends on a main-function that is executed automatically — that’s why we define our code here. As in Express, we first have to create an instance that lays the foundation for our web server.

The app.get function should also look familiar to us. The first parameter is the route; the second is the function that handles the request. Ctx stands for context and contains all important information. We can access the context with the variable c. It is similar to res in Express.js.

c.SendString() can therefore be understood like res.send().

For example, we can use the context variable to output the base URL as follows: fmt.Println(c.BaseURL()) .

Another example that shows that the context variable works like res in Express is the following. So we can set an HTTP state for our response:

app.Get("/", func(c *fiber.Ctx) error {

return c.SendStatus(404)

// "Status for Not Found"

})

Once our web server is up and running, we can use nodemon for live reloading. No, I did not mistype — nodemon; we can also use it for Go.

It works like this:

nodemon — exec go run main.go — signal SIGTERM

Pretty practical, isn’t it? The server now restarts automatically if we change anything in the code.

URL Parameters

We can’t get around this principle — URL parameters are simply super handy to allow our server to respond dynamically to requests.

In Express.js, we only have to use a special syntax in the GET route to indicate that we expect a parameter. This is how it looks like in Express:

app.get("/user/:name", (req, res) => {

res.send("Hi user " + req.params.name)

})

And surprise, that’s what it looks like in Fiber:

app.Get("/user/:name", func(c *fiber.Ctx) error {

return c.SendString("Hi User "+ c.Params("name"))

})

If we call localhost:8080/user/max, we see the following output:

That’s how easy we can pass parameters via the URL — almost exactly like in Express.js.

Serving static files

Static files are files that are no longer modified by the server — good examples are CSS or JavaScript files that we deliver to our users.

To deliver static files in Express.js, we use the middleware function express.static. In use, it looks like this:

app.use('/app', express.static('public'));

Thus, we deliver the contents of the folder “public” when a user calls the route “app”.

In Golang’s Fiber, this also looks very similar.

app.Static("/app", "./public")

The same applies here. When someone calls /app, the content of the folder “public”, which is directly in our directory, is sent to him. A typical structure is, for example, the following.

So far, so good. We see that Fiber Express.js is extremely similar. But does it also offer a real advantage? Yes, especially the performance is Fiber’s strength.

To prove that, I have implemented a benchmark. With both technologies, I have implemented the same app. To be fair, I also adjusted the response headers to be the same size in both cases.

Here is the code of the app for Fiber:

app.Get("/user/:name", func(c *fiber.Ctx) error {

c.Set("Connection", "keep-alive")

c.Set("Content-Type", "text/html; charset=utf-8")

return c.SendString("Hi User "+ c.Params("name"))

})

This is the code for the same app in Express:

app.disable("etag");

app.disable("x-powered-by");

app.get("/user/:name", (req, res) => {

res.send("Hi user "+ req.params.name)

})

The Express app is simply executed with node server.js The Fiber app, since Go is a compiler language, is compiled with go build main.go and executed with ./main .

wrk -t12 -c400 -d10s http://localhost:8080/user/max

The tool for the benchmark is called wrk. 12 is the number of threads we will use, 400 the number of concurrent connections. The load runs for 10 seconds.

The results are striking. Of course, I have made several runs, but here are the averaged results.

Fiber:

Thread Stats Avg Stdev Max +/- Stdev

Latency 2.57ms 698.98us 50.72ms 90.95%

Req/Sec 12.70k 2.10k 38.18k 96.43%

1521782 requests in 10.10s, 219.14MB read

Socket errors: connect 0, read 364, write 0, timeout 0

Requests/sec: 150603.89

Transfer/sec: 21.69MB

Express.js:



12 threads and 400 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 38.54ms 8.55ms 171.39ms 93.07%

Req/Sec 0.85k 151.82 1.00k 89.32%

102078 requests in 10.02s, 16.94MB read

Socket errors: connect 0, read 610, write 0, timeout 0

Requests/sec: 10189.99

Transfer/sec: 1.69MB Running 10s test @ http://localhost:8080/user/max 12 threads and 400 connectionsThread Stats Avg Stdev Max +/- StdevLatency 38.54ms 8.55ms 171.39ms 93.07%Req/Sec 0.85k 151.82 1.00k 89.32%102078 requests in 10.02s, 16.94MB readSocket errors: connect 0, read 610, write 0, timeout 0Requests/sec: 10189.99Transfer/sec: 1.69MB

Express.js with PM2:



12 threads and 400 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 19.77ms 25.89ms 503.95ms 93.01%

Req/Sec 2.11k 299.31 5.39k 88.73%

252083 requests in 10.03s, 41.83MB read

Socket errors: connect 0, read 151, write 0, timeout 0

Requests/sec: 25135.86

Transfer/sec: 4.17MB Running 10s test @ http://localhost:8080/user/max 12 threads and 400 connectionsThread Stats Avg Stdev Max +/- StdevLatency 19.77ms 25.89ms 503.95ms 93.01%Req/Sec 2.11k 299.31 5.39k 88.73%252083 requests in 10.03s, 41.83MB readSocket errors: connect 0, read 151, write 0, timeout 0Requests/sec: 25135.86Transfer/sec: 4.17MB

I still used PM2 because it is very useful for the production version of an app. By default, a Node.js will only use one CPU core even under load; PM2 helps with its cluster mode to reach its full potential. The results are nevertheless clear.

It is important to note that this is, only a single test — a comparison based on URL parameters. But there are so many other things that belong to real web applications, and often the performance does not depend on the backend framework but other factors. Furthermore, although fiber is more performant, the scenario of so many simultaneous requests is rather unrealistic in the real world.

Summing up

Trying out new technologies, marveling at performance is always fun.

The question is only what really remains of it. As already mentioned, one should not put too much emphasis on the results of the benchmark. In reality, it doesn’t make much sense to completely convert an existing Express.js app into Fiber — especially if you don’t have a great knowledge of Golang.

After all, every programming language brings its own problems and challenges.

I can recommend Fiber to anyone who already knows Golang or wants to implement a few projects in another language — the great advantage of the relationship between Fiber and Express is that you can immerse yourself in new technology without being completely unaware of it.

A particular advantage of fiber is that it is faster than Golang’s own net/http module. Fiber is completely based on another performance-optimized HTTP engine for Go. So if you want to implement a web server in Golang, it is advisable to use Fiber directly.

In the case of Express.js, this cannot be said. As this experiment shows, Express is not faster than a basic HTTP server in Node.js, without any frameworks or libraries. Especially smaller applications can and should be implemented without Express.js — at least from a performance point of view.