DEV Community

Cover image for Golang Web App Development
Daniel Porter
Daniel Porter

Posted on

Golang Web App Development

Introduction

Go, or Golang, is a statically typed programming language released by Google in 2009. It is a backend programming language, which I am using to build a web app. It has extensive built-in packages and features, as well as the capability of adding third party packages. Some of these include net/http, which includes the ability to listen on a port, route URLs and serve back content, html/template, which has the ability to create Go templates out of HTML pages and dynamically passed data, and database/sql, which provides the ability to connect to a database.

Using these packages, along with others, a developer can build a web app in Go.

Go has great documentation on the language and their packages, which I highly recommend checking out.

Below, I will demonstrate some of the abilities listed above, as well as some other interesting “quirks” about Go.


Structure

All files must begin with a package declaration in the first line.
To run a program, it will need a main function, like Java's main(String[] args) method.

package main

func main() {
  // contents of the main function here
}
Enter fullscreen mode Exit fullscreen mode

Declarations

From a Java perspective, much of Golang can appear backwards. For example, in Java, declaration looks like:
Java string and array

As opposed to Go's...
Golng string and slice

Go is very strict when it come to the "use it or loose it" mentality. If a variable or import is declared but not used, it must be deleted, or it will not compile.

Can't compile unused variables

Speaking of declarations, here are some examples of common declarations in Go.
Go declarations

Now, developers can declare variables using the syntax above, but there are other ways.

// declare with type
var myString string
Enter fullscreen mode Exit fullscreen mode
// declare with implied type
var myString = "Hello world"
Enter fullscreen mode Exit fullscreen mode

This last one can only be used inside a block, such as a function.

// declare with ':='
myString := "Hello world"
Enter fullscreen mode Exit fullscreen mode

Using Packages

To import a package, simply declare import(), and list the imported packages in the parenthesis.

To use functions and variables, write the package name, followed by . and the name of the thing you want.

// For example

import (
  "fmt"
)

fmt.Print("Hello World!")
Enter fullscreen mode Exit fullscreen mode

Remember, use it or loose it. The file will not compile with unused imports.

To import third-party packages as well as other local packages, you will need to create a go.mod file. In the command line, type go mod init {{project name}} to create the go.mod file.


Loops

While languages such as Java and JavaScript have for, while, and do…while loops, Go has one: the for loop. It is very easy to work with, but there are multiple ways of using it:

// The basic for loop:
for i := 0; i < 10; i++ {
}
Enter fullscreen mode Exit fullscreen mode
// The foreach loop (slices and arrays):
for index, item := range mySlice {
}
Enter fullscreen mode Exit fullscreen mode
// The foreach loop (maps):
for key, value:= range myMap {
}
Enter fullscreen mode Exit fullscreen mode
// The foreach loop (channels):
for input := range myChannel {
}
Enter fullscreen mode Exit fullscreen mode
// The while true loop:
for isTrue {
}
Enter fullscreen mode Exit fullscreen mode
// The infinite loop:
for {
}
Enter fullscreen mode Exit fullscreen mode

Access

In Java, developers mark attributes and methods as private or public to control access. Go also allows developers to control access when exporting packages, but not with public or private, but with case. If a function or variable begins with an uppercase letter, it is public. Otherwise, it is private.

var privateString = "no one can see this"
var PublicString = "other packages can see this."
Enter fullscreen mode Exit fullscreen mode

Objects (sort of)

Golang is not an Object Oriented Programming language, but it does allow for functionality similar to object creation. This can be done by declaring custom types, based on the struct, short for structure. Functions can even have receivers, making them like methods, the (u *User) in this case.

Golang custom type

With receivers, the function must be attached to a variable of the type defined in the receiver place (u *User). In the above example, one must declare a variable of type User and call the function from that.

var myUser User
myUser.WriteName
Enter fullscreen mode Exit fullscreen mode

In the functionality, the receiver is referenced with the variable u.


Arrays and Slices

We are all familiar with arrays. In languages like Java, the size of arrays are set, while in JavaScript, the size is dynamic. Golang has both. Arrays have a set length, which can not be changed. A slice is similar to an array, but it has variable length. They look very similar, but with one key difference: the value in the square brackets during declaration.

Golang array and slice declaration

A developer can also return a slice from the contents of an array/slice using index values in square brackets, returning all elements from the first listed index to the last (exclusive). The absence of a starting index means start at 0, and the absence of an ending index means end at the last element (inclusive).

// return elements at indexes [2,5)
newSlice := myArray[2:5]
Enter fullscreen mode Exit fullscreen mode
// return elements at 2 to the end
newSlice := myArray[2:]
Enter fullscreen mode Exit fullscreen mode
// return elements from the start to 5 (exclusive)
newSlice := myArray[:5]
Enter fullscreen mode Exit fullscreen mode

Random numbers

A big drawback of Golang is its random number generation. Using the math/rand package, there is a series of random value generators, but they all use the same seed, so the same numbers will be return in the same order each time. To combat this, the developer must add a seed of his own. To get a unique seed, developers use the current date in the time package, as that is always changing; thus, a different seed each time.

rand.NewSource(time.Now().UnixMicro())
randomInt := rand.Intn(10)
// return a number [0, 10)
Enter fullscreen mode Exit fullscreen mode

Functions

Functions work in Go the very similarly to Java and JavaScript. Declare with the func keyword, give the name, the parameters, the return and in the braces, give the functionality.

func equals (arg1 int, arg2 int) bool {
  return arg1 == arg2
}
Enter fullscreen mode Exit fullscreen mode

Where Go gets interesting is with the receivers, talked about under Objects, and with the ability to have multiple return values.
These are put in parentheses after the parameters. The return statement must return both types listed, and when the return is being handled, both values must be accounted for.

func RandomString(x int, n int) (string, error)
Enter fullscreen mode Exit fullscreen mode
// Handle string and error return values
output, err := util.RandomString(3, 3)
Enter fullscreen mode Exit fullscreen mode

It is very common for functions in Go to return multiple values, one usually being an error, a type similar to Java's Exceptions.

Golang will not compile with unused variables, so what do developers do if a function has multiple return values, but they only care about one? In that case, developers use the _ character. It means "I know a value is here, but I don't care about it.

// Ignore second return value
output, _ := util.RandomString(3, 3)
Enter fullscreen mode Exit fullscreen mode

Pointers

One thing to watch out for is pass-by-value. Golang is entirely pass-by-value. This means every value you pass as an argument in a function will be copied into a new variable in a new place in memory.

In other words, a variable passed as an argument will not be changed by the function, as the function is manipulating a different variable in a different place in memory.

Read this article on pass-by-value for a better description than I can give.

Pass-by-value demonstration

Developers can counteract this is by using pointers. Below, I demonstrate how to tell a function to expect a pointer and how to get a variable’s pointer.

The * before a type tells Go to expect a pointer to a value of the given type.

// Make a function that expects a pointer to a string and returns a pointer to an intager.
func usePointer (s *string) *int
Enter fullscreen mode Exit fullscreen mode

To get a pointer to a variable, use the & character before the variable.

myString := "Hello World"
usePointer(&myString)
Enter fullscreen mode Exit fullscreen mode

Reference Types

Now that I've told you that Go is entirely pass-by-value, let me tell you about the times it isn't.

Go really is entirely pass-by-value, but there are some datatypes known as reference types.
The article from Pointers talks about this.

These are types that don't directly store values (which is what value types do), but they instead store pointers. This means, even if the variable is copied, it still holds inside it a pointer to the actual value.

With reference types, developers do not need to get a pointer from them, as they contain a pointer. They can be used as normal, no pointer required!

Value and Reference types


Routing

Using the net/http package in the main() function, developers can route URLs.

http.HandleFunc("/URL", (func(res http.ResponseWriter, req *http.Request) {
        // handle responce and requests for /URL here
    }))
Enter fullscreen mode Exit fullscreen mode

HanldeFunc requires a URL pattern in a string and a handler function that has http.ResponseWriter and *http.Request parameters. The function can be stored in another file or package, but it must be properly formatted and imported.

Another option for routing is to store the routes in another file or package using http.ServeMux.
You must make sure that the Mux variable is accessible to the listener (see Listening) by either making the variable public, or by making a public function that returns a pointer to the Mux variable.

Mux := http.NewServeMux()
Mux.HandleFunc("/URL", (func(res http.ResponseWriter, req *http.Request) {
        // handle responce and requests for /URL here
    }))
Enter fullscreen mode Exit fullscreen mode

Returning Go Templates

Developers can make Go templates using HTML pages. They can be simple static pages or complex dynamic pages. That topic is too big for this blog post, but I will demonstrate how to parse and return the file in a handle function (See Routing) using the html/template package.

t, _ := template.ParseFiles("templates/index.html")
t.Execute(res, nil)
Enter fullscreen mode Exit fullscreen mode

Notice the use of the _ and the nil, which is a pointer to nothing, similar (although not the same) to the null.
The res variable used is of type http.ResponseWriter, the same one in the response handler.


Listening

The most important part in web app development is the listener. Without it, it does not matter how advanced the app is, it is useless. This is also found in the net/http package.

This is done with a very simple command in the main() function.
(These examples will listen on port 3000)

If using http.HanldeFunc for Routing:

http.ListenAndServe(":3000", nil)
Enter fullscreen mode Exit fullscreen mode

If using http.ServeMux for Routing:

mux := http.NewServeMux()
// list routing functions here

http.ListenAndServe(":3000", mux)
Enter fullscreen mode Exit fullscreen mode

Conclusion

These have been some of the basics of Golang with a very basic overview of web app development. Thank you for taking the time to read this blog post, and I would like to again recommend the Golang documentation. It is a great tool. There are also countless articles and videos about web app development. The ones I've read and seen are very good. It takes time and dedication, but it is not overly complex (just another benefit of Golang).

Top comments (0)