Tom Holloway π
Posted on August 12, 2020
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 thegofmt
command you can usego doc cmd/gofmt
for detailed information.go doc
is likeman
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 :)
Posted on August 12, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 27, 2024