DEV Community

Tom Holloway 🏕
Tom Holloway 🏕

Posted on • Updated on

Setting up a simple web server in Go with gofiber

This is the first part in a series of posts on building a full stack web application using vanilla JS, CSS, and Go. My goal is to show newcomers how to use the basics and very few dependencies to get something running across the entire stack. We want to demonstrate that we can build an entire application end-to-end including: a database, front-end single page application, components, an api, a front-end router, static content, authentication, modern reusable css and more. To start things out, we're going to build a simple web server that returns some data and basic HTML content.

Installation

You're going to need to setup Go on your system if you haven't already. Head on over to https://golang.org/ and install the latest version of Go.

You can also use gvm to manage your Go installation. https://github.com/moovweb/gvm works great for this.

Once you have gvm setup it is as simple as:

gvm install go1.15
gvm use go1.15 --default

Next up, instead of using the standard net/http that comes apart with go we are going to use gofiber https://gofiber.io/ to run our web server. Let's dig into that now.

Why gofiber?

Performance optimizations with zero-copy memory overhead is one of the reasons to use gofiber. While performance benchmarks aren't always a great reason to choose a library, mostly because you are unlikely to run into these kind of issues until your project is massive. If you run into scaling problems, that would be called a good problem to have. In any case, you can check out some of the in depth benchmarks here https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite and https://docs.gofiber.io/benchmarks. Here's just a simple comparison from that benchmark page comparing Go fiber to Express.js

Fiber      -   6,162,556 responses per second with an average latency of     2.0 ms.
Express -      367,069 responses per second with an average latency of 354.1 ms.

If you're coming from Node.js and Express you will the experience very familiar. Much of the ergonomics of fiber is modeled after express. Take a look at the simple hello world here:

package main

import "github.com/gofiber/fiber"

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

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

  app.Listen(3000)
}

Hello Go!

We want to do a bit more than hello world, so let's add a route for when someone encounters a page we don't have yet. To start, create a new folder and call it foobar. Inside foobar\ create a file called main.go. This is where we'll have our code below:

package main

import "github.com/gofiber/fiber"

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

    app.Get("/hello", func (c *fiber.Ctx) {
        c.Send("Hello world!")
    })

    app.Use(func(c *fiber.Ctx) {
        c.SendStatus(404) // Not found
    })

    app.Listen(3000)
}

To build it, you can use go build command below inside the foobar\ directory.

go build .

Fun Fact! You can use go fmt main.go to format the files. If you want to find out more about the gofmt command you can use go doc cmd/gofmt for detailed information. go doc is like man for go!

Now that you have a built package. You can run it with:

./foobar

____ / ____(_) /_  ___  _____   HOST   0.0.0.0  OS      DARWIN
_____ / /_  / / __ \/ _ \/ ___/   PORT   3000     THREADS 8
  __ / __/ / / /_/ /  __/ /       TLS    FALSE    MEM     16G
    /_/   /_/_.___/\___/_/1.13.2  ROUTES 3        PID     49104

You can check to make sure it's working with a simple curl or your browser.

curl -v localhost:3000
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 3000 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Wed, 12 Aug 2020 22:54:54 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 9
<
* Connection #0 to host localhost left intact
Not Found* Closing connection 0

And for the actual route we have:

curl -v localhost:3000/hello
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 3000 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET /hello HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 12 Aug 2020 22:55:13 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 12
<
* Connection #0 to host localhost left intact
Hello world!* Closing connection 0

Helmet

If you're coming from Express.js, you may be familiar with https://github.com/helmetjs/helmet which helps secure various HTTP headers. Fiber has a similar middleware package for helmet that does the same thing. You can set that up with:

go get -u github.com/gofiber/helmet

Then in your hello world code above:

package main

import (
    "github.com/gofiber/fiber"
    "github.com/gofiber/helmet"
)

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

    app.Use(helmet.New())

    app.Get("/hello", func (c *fiber.Ctx) {
        c.Send("Hello world!")
    })

    app.Use(func(c *fiber.Ctx) {
        c.SendStatus(404) // Not found
    })

    app.Listen(3000)
}

Conclusion

For more information, I suggest taking a deeper look at the documentation here https://docs.gofiber.io/ IMHO you should take advantage of great reference documentation as much as possible. When that doesn't work, you can go further by reading the code implementation itself. I have learned so much over my career learning how other projects work by reading their code in depth. It has helped me understand the fundamentals a lot better.
Happy coding and stay tuned for the next post in the series!

If you liked this post, check out my post on Debugging Go in VS Code! Thanks! Don't forget to like and follow :)

Discussion (0)