GO


Update Log

  • 2023 06 01 - Initial Commit

<sodipodi:namedview id=“namedview1234” pagecolor="#ffffff" bordercolor="#000000" borderopacity=“0.25” inkscape:showpageshadow=“2” inkscape:pageopacity=“0.0” inkscape:pagecheckerboard=“0” inkscape:deskcolor="#d1d1d1" showgrid=“false” inkscape:zoom=“2.0058594” inkscape:cx=“301.61635” inkscape:cy=“257.74488” inkscape:window-width=“2802” inkscape:window-height=“1694” inkscape:window-x=“4435” inkscape:window-y=“132” inkscape:window-maximized=“0” inkscape:current-layer=“svg1232” />Backspace

Overview

Golang, also known as Go, is a statically typed programming language developed by Google. Designed to be efficient, simple, and highly scalable, Go has gained popularity for its clean syntax, robust standard library, and built-in support for concurrent programming. It emphasizes simplicity and readability, making it easy to learn and write code. Go’s strong type system helps catch errors at compile-time, ensuring safer and more reliable code. With its excellent performance and low memory footprint, Go is commonly used for developing web applications, network servers, system tools, and other software projects that require efficiency and concurrency.

Why it matters

Golang was developed at Google because Rob Pike was frustrated with the state of writing software for large and complex infrastructure. He stated during a 2021 conference that dependencies were impossible to manage with any language they tried. Some major point to remember about go.

Concurrency and Scalability:

Go has built-in support for concurrency with Goroutines and channels. Goroutines allow lightweight concurrent execution, enabling developers to easily handle thousands of concurrent connections efficiently. Channels facilitate safe communication and synchronization between Goroutines, making it easier to build highly concurrent and scalable applications.

Efficiency and Performance:

Go is designed to be fast and efficient. It compiles to machine code, resulting in executables that have minimal startup time and low memory footprint. Go’s garbage collector is optimized for low-latency, allowing applications to handle a large number of requests without significant performance degradation.

Simplicity and Readability:

Go emphasizes simplicity and readability in its syntax and design. It has a minimalistic approach, offering a small and clean language specification. This simplicity makes it easier to write, read, and maintain code. Go’s syntax is straightforward, reducing the cognitive load on developers and promoting code clarity.

Strong Standard Library:

Go comes with a comprehensive and powerful standard library that provides a wide range of functionality out of the box. It includes packages for networking, cryptography, file handling, JSON/XML parsing, concurrency, and much more. The standard library encourages consistent practices and reduces the reliance on third-party libraries.

Static Typing and Safety:

Go is statically typed, which means that type-checking occurs at compile-time, catching many errors before the program is executed. This promotes code reliability, early bug detection, and better refactoring capabilities. Go’s type system is expressive and allows developers to define complex types and structures easily.

Excellent Concurrency Primitives:

Go’s concurrency primitives, Goroutines and channels, provide a powerful and safe way to handle concurrent tasks. Goroutines are lightweight, allowing the creation of thousands or even millions of concurrent Goroutines without significant resource overhead. Channels facilitate communication and synchronization between Goroutines, simplifying concurrent programming.

Cross-Platform Support:

Go supports cross-platform development, allowing developers to write code once and run it on multiple platforms without significant modifications. It provides binaries for major operating systems and processor architectures, making it easy to develop applications that are compatible with various platforms.

Easy Deployment:

Go produces self-contained executables that can be deployed without any external dependencies. This simplifies the deployment process, as there is no need to install additional runtime environments or libraries on the target system. Go’s support for static linking and the ability to compile to a single binary contribute to its ease of deployment.

Built-in Testing Support:

Go has excellent support for writing unit tests and conducting test-driven development. The standard library includes a testing package that provides testing utilities and a testing framework. This built-in testing support encourages developers to write tests, leading to better code quality and maintainability.

Community and Ecosystem:

Go has a vibrant and active community. It is backed by major organizations, including Google, and has a growing ecosystem of third-party libraries and frameworks. The community-driven nature of Go fosters collaboration, knowledge sharing, and the development of high-quality open-source tools and packages.

GO API

To get started we will have to create a very simple server which can handle HTTP requests. To do this we’ll create a new file called main.go. Within this main.go file we’ll want to define 3 distinct functions. A homePage function that will handle all requests to our root URL, a handleRequests function that will match the URL path hit with a defined function and a main function which will kick off our API.

main.go

package main

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

func homePage(w http.ResponseWriter, r *http.Request){
    fmt.Fprintf(w, "Welcome to the HomePage!")
    fmt.Println("Endpoint Hit: homePage")
}

func handleRequests() {
    http.HandleFunc("/", homePage)
    log.Fatal(http.ListenAndServe(":10000", nil))
}

func main() {
    handleRequests()
}

If we run this on our machine now, we should see our very simple API start up on port 10000 if it’s not already been taken by another process. If we now navigate to http://localhost:10000/ in our local browser we should see Welcome to the HomePage! print out on our screen. This means we have successfully created the base from which we’ll build our REST API.

Our Articles Structure

We’ll be creating a REST API that allows us to CREATE, READ, UPDATE and DELETE the articles on our website. When we talk about CRUD APIs we are referring to an API that can handle all of these tasks: Creating, Reading, Updating and Deleting.

Before we can get started, we’ll have to define our Article structure. Go has this concept of structs that are perfect for just this scenario. Let’s create an Article struct that features a Title, a Description (desc) and Content like so:

type Article struct {
    Title string `json:"Title"`
    Desc string `json:"desc"`
    Content string `json:"content"`
}

// let's declare a global Articles array
// that we can then populate in our main function
// to simulate a database
var Articles []Article

Our Struct contains the 3 properties we need to represent all of the articles on our site. In order for this to work, we’ll also have to import the “encoding/json” package into our list of imports.

Let’s now update our main function so that our Articles variable is populated with some dummy data that we can retrieve and modify later on.

func main() {
    Articles = []Article{
        Article{Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

Perfect, let’s now move on to creating our /articles endpoint which will return all of the articles that we’ve just defined here.


Go Packaging Modules

  1. Initialize a new go module

    go mod init github.com/trevorsmale/example

  2. Create repository, matching name of repo to name of module

  3. Create initial go file with module functions

    package mylogger

    import “log”

    func LogInfo(message string) { log.Printf(“INFO - %v”, message) }

  4. Commit to github

Back to Top


Routers

Now the standard library is adequate at providing everything you need to get your own simple REST API up and running but now that we’ve got the basic concepts down I feel it’s time to introduce third-party router packages. The most notable and highly used is the gorilla/mux router which, as it stands currently has 2,281 stars on Github.

Building our Router

We can update our existing main.go file and swap in a gorilla/mux based HTTP router in place of the standard library one which was present before.

Modify your handleRequests function so that it creates a new router.

main.go

package main

import (
    "fmt"
    "log"
    "net/http"
    "encoding/json"
    "github.com/gorilla/mux"
)

… // Existing code from above
func handleRequests() {
    // creates a new instance of a mux router
    myRouter := mux.NewRouter().StrictSlash(true)
    // replace http.HandleFunc with myRouter.HandleFunc
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/all", returnAllArticles)
    // finally, instead of passing in nil, we want
    // to pass in our newly created router as the second
    // argument
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

func main() {
    fmt.Println("Rest API v2.0 - Mux Routers")
    Articles = []Article{
        Article{Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

When you now run this, you will see no real change to the way our system works. It will still start up on the same port and return the same results depending on what endpoints you hit.

The only real difference is that we now have a gorilla/mux router which will allow us to easily do things such as retrieve path and query parameters later on in this tutorial.

$ go run main.go

Rest API v2.0 - Mux Routers

Path Variables

So far so good, we’ve created a very simple REST API that returns a homepage and all our Articles. But what happens if we want to just view one article?

Well, thanks to the gorilla mux router we can add variables to our paths and then pick and choose what articles we want to return based on these variables. Create a new route within your handleRequests() function just below our /articles route:

myRouter.HandleFunc("/article/{id}", returnSingleArticle)

Notice that we’ve added {id} to our path. This will represent our id variable that we’ll be able to use when we wish to return only the article that features that exact key. For now, our Article struct doesn’t feature an Id property. Let’s add that now:

type Article struct {
    Id      string `json:"Id"`
    Title   string `json:"Title"`
    Desc    string `json:"desc"`
    Content string `json:"content"`
}

We can then update our main function to populate our Id values in our Articles array:

func main() {
    Articles = []Article{
        Article{Id: "1", Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Id: "2", Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

Now that we’ve done that, in our returnSingleArticle function we can obtain this {id} value from our URL and we can return the article that matches this criteria. As we haven’t stored our data anywhere we’ll just be returning the Id that was passed to the browser.

func returnSingleArticle(w http.ResponseWriter, r *http.Request){
    vars := mux.Vars(r)
    key := vars["id"]

    fmt.Fprintf(w, "Key: " + key)
}

If we navigate to http://localhost:1000/article/1after we’ve now run this, you should see Key: 1 being printed out within the browser.

Let’s use this key value to return the specific article that matches that key.

func returnSingleArticle(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    key := vars["id"]

    // Loop over all of our Articles
    // if the article.Id equals the key we pass in
    // return the article encoded as JSON
    for _, article := range Articles {
        if article.Id == key {
            json.NewEncoder(w).Encode(article)
        }
    }
}

Back to Top


Binaries

Why Go Executables are so damn big

Go binaries are standalone Everything is statically linked Memory management Symbol tables External packages

The size of the Bin can be reduced by removing debug and symbol table data

Downsides

May cause bugs when: working with reflections working with servers and HTTP

Packaging

Go projects can be packaged into single executable binaries. Go is also capable of building other frameworks into the binary. REACT front end applications can be packaged with GO and a Database to form a full application. PocketBase is a great example project that combines Svelt + REACT + GO + SQL LIGHT to form a full service package including payment processing.

Be aware of

CG enabled option

CRUD Create Read Update Delete

A method to transmit changes to a database

Create – Uses the HTTP POST method to add one or more records. Read – Uses the HTTP GET method to retrieve one or more records * that match certain criteria. Update – Uses the HTTP PUT or PATCH methods to update a record. Delete – Uses the HTTP DELETE method to remove one or more records.

GORM Object-Relational Mapping

is a fantastic object-relational mapper (ORM) library for Golang that claims to help developers build faster and make fewer errors.

The GORM library is built on top of the Golang database/sql package. That means it only works with relational databases (MySQL, PostgreSQL, SQLite, etc). Like other ORMs, GORM also provides a lot of tools to help developers interact with databases with ease.

package models

import (
    "time"

    "github.com/google/uuid"
)

type Post struct {
    ID        uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4();primary_key" json:"id,omitempty"`
    Title     string    `gorm:"uniqueIndex;not null" json:"title,omitempty"`
    Content   string    `gorm:"not null" json:"content,omitempty"`
    Image     string    `gorm:"not null" json:"image,omitempty"`
    User      uuid.UUID `gorm:"not null" json:"user,omitempty"`
    CreatedAt time.Time `gorm:"not null" json:"created_at,omitempty"`
    UpdatedAt time.Time `gorm:"not null" json:"updated_at,omitempty"`
}

type CreatePostRequest struct {
    Title     string    `json:"title"  binding:"required"`
    Content   string    `json:"content" binding:"required"`
    Image     string    `json:"image" binding:"required"`
    User      string    `json:"user,omitempty"`
    CreatedAt time.Time `json:"created_at,omitempty"`
    UpdatedAt time.Time `json:"updated_at,omitempty"`
}

type UpdatePost struct {
    Title     string    `json:"title,omitempty"`
    Content   string    `json:"content,omitempty"`
    Image     string    `json:"image,omitempty"`
    User      string    `json:"user,omitempty"`
    CreateAt  time.Time `json:"created_at,omitempty"`
    UpdatedAt time.Time `json:"updated_at,omitempty"`
}

Retrieving

In this part of the tutorial we are going to create a new REST endpoint which, when hit with a HTTP GET request, will return all of the articles for our site.

We’ll first start off by creating a new function called returnAllArticles, which will do the simple task of returning our newly populated Articles variable, encoded in JSON format:

main.go

func returnAllArticles(w http.ResponseWriter, r *http.Request){
    fmt.Println("Endpoint Hit: returnAllArticles")
    json.NewEncoder(w).Encode(Articles)
}

The call to json.NewEncoder(w).Encode(article) does the job of encoding our articles array into a JSON string and then writing as part of our response.

Before this will work, we’ll also need to add a new route to our handleRequests function that will map any calls to http://localhost:10000/articles to our newly defined function.

func handleRequests() {
    http.HandleFunc("/", homePage)
    // add our articles route and map it to our 
    // returnAllArticles function like so
    http.HandleFunc("/articles", returnAllArticles)
    log.Fatal(http.ListenAndServe(":10000", nil))
}

Now that we’ve done this, run the code by typing go run main.go and then open up http://localhost:10000/articles in your browser and you should see a JSON representation of your list of articles like so:

http://localhost:10000/articles response

[
{
    Title: "Hello",
    desc: "Article Description",
    content: "Article Content"
},
{
    Title: "Hello 2",
    desc: "Article Description",
    content: "Article Content"
}
];

We’ve successfully defined our first API endpoint.

In the next part of this series, you are going to update your REST API to use a gorilla/mux router instead of the traditional net/http router.

Swapping the routers will enable you to more easily perform tasks such as parsing any path or query parameters that may reside within an incoming HTTP request which we will need later on.

Basic Server

Use Go version 1.11 or higher.

Create the following files and folders according to the structure below. The file server.go sits at the root of your project, as does the static folder, which contains two HTML files: index.html and form.html.

- server.go
- static/
- - index.html
- - form.html

Open the server.go file and import the required packages. Use fmt to print useful data to the terminal and log to print fatal errors in case the web server crashes.

The net/http is the most important package. It provides all the functionality for creating an HTTP client or server implementation such as a Golang web server.

package main

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

Lastly, let’s add a simple main() function in the server.go file that prints a message to the terminal.

func main() {
    fmt.Printf("Starting server at port 8080\n")
}

To test the setup, start the fictive server with the following command.

go run server.go

JSON

Importing, Unmarshalling and Interpreting JSON string data

Let’s assume a simple file config.json, having the following content:

{
    "origin": "golangdocs.com",
    "user": "Vijay",
    "active": true
}

Let’s now extract the JSON data from this config.json file.

// main.go
package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
)

// The data struct for the decoded data
// Notice that all fields must be exportable!
type Data struct {
    Origin string
    User   string
    Active bool
}

func main() {
    // Let's first read the `config.json` file
    content, err := ioutil.ReadFile("./config.json")
    if err != nil {
        log.Fatal("Error when opening file: ", err)
    }

    // Now let's unmarshall the data into `payload`
    var payload Data
    err = json.Unmarshal(content, &payload)
    if err != nil {
        log.Fatal("Error during Unmarshal(): ", err)
    }

    // Let's print the unmarshalled data!
    log.Printf("origin: %s\n", payload.Origin)
    log.Printf("user: %s\n", payload.User)
    log.Printf("status: %t\n", payload.Active)
}

You should get the below output, after creating config.json.

Sample Output

2021/02/07 16:53:53 origin: golangdocs.com
2021/02/07 16:53:53 user: Vijay
2021/02/07 16:53:53 status: true

Seems straightforward, but you must be a bit careful about one thing, if you’re a newcomer to go.

Golang has a concept of “exported” fields in struct datatypes and functions, so this essentially means that these exported fields are the ones which will be visible across different packages. Since the json unmarshal function is external, it can only see exportable fields.

To make a field exportable, just ensure that you capitalize it! That’s why it’s Origin, User and Active, as opposed to origin, user and active.

Reading Unstructured Data from JSON Files

If the contents of our config.json file keep changing regularly, it is practically impossible to keep track of the changes by modifying the struct fields again and again.

To simplify this, we can use the concept of encoding arbitrary data as an interface. We can replicate the JSON structure by visualizing it as a key-value map.

Here, the key will be a string, and the value can be any interface{}.

So we can simply modify the Data struct into a map[string]interface{}!

// main.go
package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
)

func main() {
    // Let's first read the `config.json` file
    content, err := ioutil.ReadFile("./config.json")
    if err != nil {
        log.Fatal("Error when opening file: ", err)
    }

    // Now let's unmarshall the data into `payload`
    var payload map[string]interface{}
    err = json.Unmarshal(content, &payload)
    if err != nil {
        log.Fatal("Error during Unmarshal(): ", err)
    }

    // Let's print the unmarshalled data!
    log.Printf("origin: %s\n", payload["origin"])
    log.Printf("user: %s\n", payload["user"])
    log.Printf("status: %t\n", payload["active"])
}

Notice now that we can store arbitrary data easily, due to the interface{} map!