Create a Golang Web App without using any Framework

Create a Golang Web App without using any Framework

Use the net/http library to create a web application without using any framework

When it comes to making a web app in Golang, I have often seen tutorials that were taught in Gin, Beego, Fiber, etc. Beginners have no clue to make a simple Golang web app without using any framework.

When I was first learning Golang, I made a few simple projects but when it came to making a web app, I didn't find many resources online.

Most of the resources were focused on the frameworks. For me, it was really difficult to learn them. Finally, after reading some books and the code line by line, I understood the concept of making a web application in Golang. Unfortunately, the books are paid and there are limited resources online.

Here is a blog that will help you create a Golang web app without using any framework.

Note: I am going to briefly explain each and every step in this blog. But I won't attach the browser screenshots as a result. It's your homework to do it.

Prerequisites

Implementation

After installing the Golang, open the Visual Studio Code and create a main.go file. Inside the main.go file, write the following code.

main.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func HomePage(w http.ResponseWriter) {
    fmt.Fprintln(w, "<h1> Home page </h1>")
}

func AboutPage(w http.ResponseWriter) {
    fmt.Fprintln(w, "<h1> About page </h1>")
}

func ContactPage(w http.ResponseWriter) {
    fmt.Fprintln(w, "<h1> Contact page </h1>")
}

func main() {
    http.HandleFunc("/", HomePage)
    http.HandleFunc("/about", AboutPage)
    http.HandleFunc("/contact", ContactPage)
    fmt.Println("Starting the server at port: 8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal(err)
    }
}
  • This file has four functions. Three of them are the functions of different pages and the last one is the main() function that will run all the above three functions.

  • The HomePage, AboutPage and ContactPage functions contain the write parameter (w http.ResponseWriter) that is used to print the data on the browser.

  • fmt.Fprintln() will write the data on the browser.

  • Every function is called in the main function using the http.HandleFunc() with a link and a function name.

  • fmt.Println() will print the message when the code will run.

  • At the end, http.ListenAndServe is used that will contain the port number. You can give any port number you want. I gave the port number 8080.

  • After writing this code, run it in the terminal by typing go run main.go and it will give you a message Starting the server at port: 8080. Once this message is given, type localhost:8080 in the browser and you will see the home page.

  • You can move to other pages also by typing localhost:8080/about or localhost:8080/contact.

Adding if-statement and read parameter

main.go

func HomePage(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
          fmt.Fprintln(w, "<h1> Page not found </h1>")
          return
    }
    fmt.Fprintln(w, "<h1> Home page </h1>")
}
  • r *http.Request is added to read the data.

  • if r.URL.Path != "/" is used to stop running other links. If the given links like /, /about, /contact are not visited or if other links are visited like localhost:8080/hello or localhost:8080/try etc, it will give an error message that the page is not found.

Using HTML templates in Golang

views/templates/home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title> Home </title>
</head>
<body>
    <h1>Home Page</h1>
    <h1>This is the home page</h1>
</body>
</html>

main.go

package main

import (
    "fmt"
    "net/http"
    "html/template"
)

var homeTmpl = template.Must(template.ParseFiles("views/templates/home.html"))
var aboutTmpl = template.Must(template.ParseFiles("views/templates/about.html"))
var contactTmpl = template.Must(template.ParseFiles("views/templates/contact.html"))

func HomePage(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
          fmt.Fprintln(w, "<h1> Page not found </h1>")
          return
    }
    homeTmpl.Execute(w, nil)
}

func AboutPage(w http.ResponseWriter) {
    aboutTmpl.Execute(w, nil)
}

func ContactPage(w http.ResponseWriter) {
    contactTmpl.Execute(w, nil)
}

...
  • Make a views directory. Inside the views, create a subdirectory called templates. Inside the templates, create three HTML files by the name of home.html, about.html, and contact.html.

  • Write the same HTML code in all the files that I have written above in the home.html but just change the title and the body text in each file.

  • After that, move to the main.go file and change the code a little bit. Write the file paths inside the template.Must(template.ParseFiles() to fetch the data from those files and make them equal to different variable names like homeTmpl, aboutTmpl, and contactTmpl.

  • Once these variables are created, remove the fmt.Fprintln() from each function and instead take the variable names that are just created and write Execute(w, nil) with it. It will execute those files.

  • Run the code to start the server again and type localhost:8080 in the browser. You will see that the HTML file text is executed in the browser. Move to other pages. You will also see the other files' execution.

Problem No. 1

main.go

...

func MakeTemplate(path string) template.Template {
    return *template.Must(template.ParseFiles(path))
}

var homeTmpl = MakeTemplate("views/templates/home.html")
var aboutTmpl = MakeTemplate("views/templates/about.html")
var contactTmpl = MakeTemplate("views/templates/contact.html")

...
  • If there are multiple pages then template.Must(template.ParseFiles() is going to be used multiple times. To avoid this problem, I created a function by the name of MakeTemplate() and inside it, I just returned this common code *template.Must(template.ParseFiles(path)) so that it is not used multiple times.

  • Just give a path as an argument in the MakeTemplate() function and you're good to go.

Problem No. 2

views/templates/base.html

{{define "base"}}    

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title> {{template "title" .}} </title>
</head>
<body>
    <section>
        {{template "body" .}}
    </section>
</body>
</html>

{{end}}

views/templates/home.html

{{template "base" .}}

{{define "title"}} Home {{end}}

{{define "body"}}
    <h1> Home Page </h1>
    <p> This is the home page </p> 
{{end}}

views/templates/about.html

{{template "base" .}}

{{define "title"}} About {{end}}

{{define "body"}}
    <h1> About Page </h1>
    <p> This is an about page </p> 
{{end}}  

views/templates/contact.html

{{template "base" .}}

{{define "title"}} Contact {{end}}

{{define "body"}}
    <h1> Contact Page </h1>
    <p> This is the contact page </p> 
{{end}}   

main.go

...

func MakeTemplate(path string) template.Template {
    files := []string{path, "views/templates/base.html"}
    return *template.Must(template.ParseFiles(files...))
}

...       
  • Most of the HTML code is common in all the templates. To solve this problem, there is a need to create a base.html file that will contain that common code and we just need to call that template to use it.

  • In the base.html file, I used {{define "base"}} ... {{end}}. It means that the code/template inside the {{define "base"}} ... {{end}} is able to be used in all the HTML files. define "base" is like a variable that is equal to the whole template. Now I just need to call this variable in other HTML files to use it.

  • In the base.html file, I also used {{template "title" .}} and {{template "body" .}} to call the title and body that are defined in other template files(home.html, about.html, and contact.html).

  • Now come to the home.html, about.html, and contact.html files. Inside each of them, you can see that I called {{template "base" .}} that I defined in the base.html file. It will use the HTML template of the base.html inside all the other HTML files.

  • I defined the title and body in each file separately. That data is already called in the base.html file by using the {{template "title" .}} and {{template "body" .}}.

  • In the main.go file, I wrote files := []string{path, "views/templates/base.html"} inside the MakeTemplate() function. You can see that every path variable is linked to the base.html file path. It means that if any path is running, then the base.html file will also run with it. I make this line of code equal to the files variable.

  • Use this files variable while returning the code and the three dots ... mean to find all the files recursively.

I hope you like this blog. If you like it then make sure to share it with other people to help them. You can also connect with me on Twitter.

Thank you!