Building a Lightweight Web Server with Golang
Published: 2022-10-01 13:36:46

---
title: Building a Lightweight Web Server with Golang
published: true
date: 2022-10-01 12:41:26 UTC
tags: 
canonical_url: https://thecodeway.hashnode.dev/building-a-lightweight-web-server-with-golang
---

![1-web-server-3edc7d64437aeed2116ff819898d2d4d.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1664627423595/hN8TZQbc_.png)

Let's have some fun with Golang by building a simple website application server. Golang is a very robust and fast programming language. We will build this purely with only Golang no extra dependencies.

To follow this tutorial, you need to have basic knowledge of Golang programming. Visit [Go Tour](https://go.dev/tour/welcome/1) to quickly get a basic understanding of programming Golang.

The goal is to write a Go program that serves HTML, CSS, JS, and static files from a root directory. For example, if we have a directory structure like below

```
project
 website
    index.html
    file1.html
| file2.html
| assets
        style.css
        script.js
 main.go

```

When we run main.go with port `8000`, we would be able to visit the following URLs

- http://localhost:8000/
- http://localhost:8000/index.html
- http://localhost:8000/file1.html
- http://localhost:8000/assets/style.css

The website directory is the root directory. We can place all our HTML files and static files in that directory. Then we can access them with the URL `http://localhost:8000/`. This URL represents our root directory, `website`. While building the web server we can decide

- which directory to be the root folder,
- what the URL should be, and
- how files are processed.

## Why Golang

Languages like Python requires developers to depend on web frameworks like Django or Flask to build web applications. Golang has a lot of built-in functionality for building web apps, like;

- Handling routes,
- Database connections,
- Template rendering,
- Testing,
- and more.

Golang requires fewer to no dependencies installed to build a website application.

## Get set, Go! 🛩

### Install Golang

Install Golang by visiting [Go.dev](https://go.dev/learn/) to install Golang on your machine. The installation binaries are available for any Operating System (Windows, Mac, Linux, etc.).

### Setup project directory

To setup our project directory,

- create a new directory/folder for the web server project,
- create a file named `main.go`, and
- open the file in any editor of choice.

### Let's build 💪🏽

Paste the following content in the `main.go` file.

```
package main

func main(){

}

```

Every Go program is made up of packages and Programs start running in package `main`. The function `main` is executed when we run our `main.go` file or its build.

Create a function called `ServeStatic` that registers the pattern `/` to a `handler function` that serves static contents in the root folder. The pattern is registered in the default `DefaultServeMux`. We could also create our own [ServeMux](https://pkg.go.dev/net/http?utm_source=gopls#ServeMux), and register patterns to it. We could have multiple ServeMux depending on your requirement.

```
import "net/http"

// Provides handler function to read and respond with static files
func ServeStatic() {
    h1 := func(w http.ResponseWriter, r *http.Request) {
        // code here
    }
    http.HandleFunc("/", h1)
}

```

Take a look at `http.HandleFunc("/", h1)`, the first parameter is the pattern, and the second parameter is the handler function. http.HandleFunc registers the pattern, `/`, to the handler function, `h1`.

DefaultServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.

We use the pattern "/" because it matches all paths not matched by other registered patterns, not just the URL with Path == "/". Meaning the pattern, "/" matches URLs paths

- /
- /anything
- /a.html
- /a/b/c.html
- /a/b/c/.../z.html

When we visit paths like `/blog/posts/web-developments/how-to-build-a-website.html` our handler function will be executed, because its pattern "/" matches it.

The handle function is executed with two parameters, a http.ResponseWriter and a \*[http.Request](https://pkg.go.dev/net/http?utm_source=gopls#Request). We can use the ResponseWriter to construct an HTTP response. The Request represents an HTTP request received from the server. It contains data like URL path, Request headers, Request body, the Request method, etc.

The static file path can be extracted from the URL Path, then we can get the content from the file path and use it to construct an HTTP response with our ResponseWriter.

Let's create a function to construct the file path relative to a root folder called `website`. We can use any directory as our root folder. I am using this for simplicity.

```
// Gets the relative path of the requested path, making `website` the base path
func RelPath(path string) string {
    return "website" + path
}

```

This is my directory structure,

```
go_server
 website
    index.html
    about.html
| main.css
| contact.html
 main.go

```

Therefore, if the path is `/about.html`, the file path relative to the root folder, website, is `website/about.html`.

For example, if our directory structure is like the below.

```
go_server
 website
    public
        index.html
        about.html
| main.css
| contact.html
 main.go

```

If we wanted the public folder to be the root folder, then our `RelPath` function should look like this

```
func RelPath(path string) string {
    return "website/public" + path
}

```

The complete ServeStatic function is below.

```
import "net/http"

// Provides handler function to read and respond with static files
func ServeStatic() {
    h1 := func(w http.ResponseWriter, r *http.Request) {
        path := RelPath(r.URL.Path)
        http.ServeFile(w, r, path)
    }
    http.HandleFunc("/", h1)
}

```

[http.ServeFile](https://pkg.go.dev/net/http?utm_source=gopls#ServeFile) is a function that replies to the request with the contents of the named file or directory. After the execution of `RelPath(r.URL.Path)`, the variable, path, will be a file path relative to the root folder. The program is able to access this folder because the `main.go` file is in the same directory as the root folder.

If the path does not exist, a `404 page not found` response is written to the ResponseWriter. Read more on [http.ServeFile](https://pkg.go.dev/net/http?utm_source=gopls#ServeFile) to see how it handles paths.

Finally, We need to listen to requests on a [TCP](https://en.wikipedia.org/wiki/Transmission_Control_Protocol) network address.

```
import "log"

// Listen and serve port
func StartServer(addr string) {
    log.Printf("Starting server listening on: http://localhost:%v", addr)
    log.Fatal(http.ListenAndServe(":"+addr, nil))
}

```

StartServer function accepts a network address, addr, to use with [http.ListenAndServe](https://pkg.go.dev/net/http?utm_source=gopls#ListenAndServe). The ListenAndServe function accepts an addr, string, and a handler, http.Handler.

ListenAndServe listens to the TCP network address, addr, and then calls Serve with the handler to handle requests on incoming connections. The handler is typically nil, in which case the DefaultServeMux is used. You can create your own ServeMux to use here instead.

For example, if addr is `6500`, then the URL will be `http://localhost:6500`. So any time a request is sent to the URL, the DefaultServeMux handles it.

Now we update our `main` function to execute the `ServeStatic` and `StartServer` functions.

```
func main() {

    ServeStatic()
    StartServer("8000")

}

```

Here we used `8000` as our TCP network address, therefore our URL will be `http://localhost:8000`.

This is what the complete `main.go` file should look like

```
package main

import (
    "log"
    "net/http"
)

func main() {

    ServeStatic()
    StartServer("8000")

}

// Listen and serve port
func StartServer(addr string) {
    log.Printf("Starting server listening on: http://localhost:%v", addr)
    log.Fatal(http.ListenAndServe(":"+addr, nil))
}

// Gets the relative path of the requested path, making `website` the base path
func RelPath(path string) string {
    return "website" + path
}

// Provides handler function to read and respond with static files
func ServeStatic() {
    h1 := func(w http.ResponseWriter, r *http.Request) {
        path := RelPath(r.URL.Path)
        http.ServeFile(w, r, path)
    }
    http.HandleFunc("/", h1)
}

```

Check out this [repo](https://github.com/devvspaces/golang_web_server) for the complete project. It has sample HTML and CSS files to use with the web server.

[https://github.com/devvspaces/golang\_web\_server](https://github.com/devvspaces/golang_web_server)

## Conclusion 🎯

You can do much with Golang's standard HTTP [library](https://pkg.go.dev/net/htttp). For sample projects, you can build a website form, polling website, todo website, CRUD website, Rest APIs, and more

The best place to learn and get more information about Golang libraries & packages is their [documentation](https://pkg.go.dev/). It has robust documentation with examples.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wt570kpytt4hsctjxwuq.gif)