Moving from NodeJS to Go

  • 2018-09-17 06:55 AM
  • 2095

Since this is a NodeJS to Go comparison, we're going to focus on language constructs and servers

The approach of this article is this:

You are a seasoned NodeJS developer, and are looking to learn a new language, but you don't want to go deep, just see how things compare with your current expertise, and then make a final decision.

Chances are, you were initially a PHP developer, then you found your way into NodeJS and now you feel like you want to expand your expertise. Of course I might be wrong, but in this article we are going to look at some common patterns when working with NodeJS and how they compare in Go.

Prerequisites

  1. You must have an understanding of language constructs such as conditional statements and loops. They are usually very similar across programming languages.
  2. You should have an understanding of NodeJS.
  3. Understanding Golang is not really a requirement, as we’ll be mostly comparing language constructs, but you can always take the tour of go.

Approach

Since this is a NodeJS to Go comparison, we're going to focus on language constructs and servers. We'll break it down like this:

  1. Common Language Constructs
  2. HTTP Servers
  3. Middleware
  4. Consuming APIs
  5. Database Connections

Why Go

The most common reasons to learn Go include the following:

  1. Simplicity - I got the diagram below from this github rep:Keywords per language
We see that Go has only 25 keywords, and this can give you a clue on how simple it is to learn the language.

We'll also be looking at the common language patterns, and we'll see how cleaner and easier it is to do some common tasks as compared to other languages.

The standard library that comes with Go also has a lot of built in functionality, that you will find yourself rarely relying on other external libraries.

Go code will not compile if you have unused variables or syntax errors.
  1. Performance - Go is really good when it comes to performance because it’s compiled to machine code. Unlike JavaScript or PHP where we need interpreters or an environment where the code can run, Go compiles programs to a single binary, and runs them on a bare machine.

  2. Concurrency - While other languages have their own way of handling concurrency, Go’s approach is much simpler and cleaner, and built-in. This is one of the main reasons why some people prefer Go, as we can all argue that the first two points depend on the proficiency of the developer.

  3. Documentation - The entirety of Go’s standard library documentation is generated from code, and exists in a dedicated site at https://golang.org/pkg/. Additionally, if you visit https://godoc.org/ you can type in the name of any package, standard library or not, and look up it’s code generated documentation. The home page lists popular packages, which are open source.

Here’s an example with the net/http package that we’ll use later. You can see examples are provided, and there are even tabs for the source code files. Sweet!

Is Go Better Than JavaScript ?. No, I don't think so, nor do I think JavaScript is better. The idea is to use the right tool for the job, and sometimes, one tool is better than another one, or one developer is better in one tool over another. So learn what you can, and choose the right thing that you are sure will get the job done for you.

Language Constructs

We'll look at a few language constructs that we are used to.

Hello World

Here's a hello world program in NodeJS

console.log('Hello World');

Running this will produce Hello World in the terminal.

node app.js

Here's an equivalent hello world program in Go

package main

import "fmt"

func main() {
    fmt.Println("Hello World")
}

What we'll notice first, is that Go follows a certain structure which involves:

  1. You must have an understanding of language constructs such as conditional statements and loops. They are usually very similar across programming languages.
  2. You should have an understanding of NodeJS.
  3. Understanding Golang is not really a requirement, as we’ll be mostly comparing language constructs, but you can always take the tour of go.

This code can be run by typing in the following, or see it in GoPlay Space.

go run main.go

where main.go is the name of the file.

Data Types, Variables and Constants

JavaScript is dynamically typed, which means you do not have to specify types when defining variables, and the variables can change their types as you program along.

That being said, JavaScript has the following types:

To define variables in JavaScript(NodeJS) we'd write this:

const num = 3;
let flt = 2.34;
let a = true;
let b = false;
var name = 'Scotch';

Go however, is statically typed, which means we have to define the types before hand, or assign them and let them be inferred. Here’s a comprehensive list of Go Types.

An equivalent of the above JavaScript definitions in Go is

package main

const num = 3

func main() {
    var flt float64 = 2.34
    var name string = "Scotch"
    a, b := 2.34 // shorthand declaration syntax
}

Assigning initial values in Go is done by with the var keyword, a variable name, and a type. Additionally and more commonly the := shorthand syntax can be used. When declaring variables with := , they are automatically assigned the correct type. The shorthand declaration syntax can only be used within a function. Finally, you can assign multiple values in one line like we have done in a, b := true, false. This will assign both a and b to the right hand values respectively.

Here is an array of strings in JavaScript

const names = ['Scotch', 'IO'];

While Go has arrays, what we typically refer to arrays in JavaScript are referred to as slices in Go. Here’s an example of a slice in Go:

package main

func main() {
    names := []string{"Scotch", "IO"}
}

As an example, we'll write a small program that returns the substring of a string at index 10. So a sentence like Luke, I'm not your father will end up being Luke, I'm

JavaScript

const sentence = '`Look, I\'m not your Father';`
console.log(sentence.substr(0,10));

Go

package main

import "fmt"

func main() {
    sentence := "Look, I'm not your Father"
    fmt.Println(sentence[:10])
}

You can run the app in Goplay Space

Conditional Statements

Conditional statements include if else and switch statements. Here's an example in NodeJS.

const age = 10;

if (age > 10) {
    console.log('You are old');
}

console.log('you are young');

const gender = 'female';

switch(gender) {
    case 'female':
        console.log('your name is tonie');
        break;
    case 'male':
        console.log('your name is tony');
        break;;
    default:
        ///
}

Here's a equivalent Go

package main

import "fmt"

func main() {
    age := 10
    if age > 10 {
        fmt.Println("You are old")
    }
    fmt.Println("You are young")

    gender := "female"
    switch gender {
    case "female":
        fmt.Println("Your name is tonie ")
    case "male":
        fmt.Println("Your name is tony")
    default:
        ///
    }
}

You can run the app in GoPlay Space.

You'll notice the conditionals are a bit cleaner in Golang, with less brackets.

Loops

JavaScript has 3 loops: for loop, while loop, and a do while loop. Here's a for loop example.


// normal for loop
for i = 0; i < 10; i++ {
    console.log(i);
}

// key, value loop
for (var key in p) {
    if (p.hasOwnProperty(key)) {
        console.log(key + &apos; -> &apos; + p[key]);
    }
}

// another key value loop
Object.keys(obj).forEach(function(key) {
    console.log(key, obj[key]);
})

// there&apos;s also a `for...of`

Go has only one type of loop, and it's the for loop. Don't let that deceive you though as the for loop in Go is very versatile and can emulate almost any type of loop. Let's look at a simple example:

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }

    // key value pairs
    kvs := map[string]string{
        "name":    "Scotch",
        "website": "https://scotch.io",
    }

    for key, value := range kvs {
        fmt.Println(key, value)
    }
}

You can run the app in GoPlay Space.

Http Server

Now that we know a little about the similarities and differences in language constructs, we can have a look at servers. Since we are coming from NodeJS it's likely that we're building a server, that returns JSON for instance.

In NodeJS, chances are while writing a server, you are using Express as the base library for your server. It’s the most common, comes with a router and is the most battle tested. Here’s a NodeJS server.

const express = require(&apos;express&apos;);
const app = express();

app.get(&apos;/&apos;, (req, res) => {
  res.send(&apos;Hello from Express!&apos;);
})

app.listen(3000, err => {
  if (err) {
    return console.log(&apos;something bad happened&apos;, err);
  }

  console.log(`server is listening on 3000`);
})

Unlike JavaScript, the Go standard library provides everything we need to get a server up and running without any external dependencies. The net/http package provides most of this functionality. When building larger applications, expanding on the base net/http package with third-party packages is common, and one popular library that provides greatly expanded functionality is the Gorilla Web Toolkit.

Here's an equivalent Server in Go without any external library.

package main

import (
    "net/http"
)

func Hello(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World"))
}
func main() {
    http.HandleFunc("/", Hello)
    if err := http.ListenAndServe(":8080", nil); err != nil {
        panic(err)
    }
}

Things are starting to get interesting now.

  • We've imported the [net/http](https://godoc.org/net/http) package which will help us handle requests.
  • We then create a handler function, which takes in a response, and a request pointer. Unlike NodeJs/Express, the order here is res, req, and not req, res.
  • ResponseWriter is an interface used by http to construct a response. In your Go journey, you are going to come across a lot of interfaces, which at first may seem unfamiliar. Interfaces usually have to implement some methods that are predefined, and in this case, ResponseWriter has a Write message that helps write to the output stream, which in this case is the browser.
  • Since Write expects an slice of bytes, we need to convert the Hello World string to a slice of bytes. There are other alternatives to this, such as fmt.Fprint(w, "Hello World"), which takes in a writer, and a string, but will require us to import “fmt”.
  • We finally have a main function, that calls net/http's HandleFunc, which takes in a pattern string, and the Handler function we defined earlier.
  • The last step is calling http.ListenAndServer(), and checking if there's any error. You will see this pattern a lot in Golang
if err != nil {
    // do something here
    // return err or panic
    panic(err)
}

You can test this by running go run main.go, assuming your file was named main.go

Now, let's introduce a router library, because, if we don't, we'll have a lot of this in our code.

if req.Method == "POST" {
    // do this
}

To get packages with golang, you usually use a go get <github-link>. To get Gorilla Mux from the Gorilla Web Toolkit we mentioned earlier, we would write the following in our terminal:

go get -u github.com/gorilla/mux

Then we are able to do this. I pulled this directly from Gorilla mux documentation.

package main

import (
     "fmt"
     "net/http"

     "github.com/gorilla/mux"
)
func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    r.HandleFunc("/products", ProductsHandler)
    r.HandleFunc("/articles", ArticlesHandler)

    // details
    r.HandleFunc("/products/{key}", ProductHandler)
    r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
    r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)

    http.Handle("/", r)
}

// example handler
func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, "Category: %v\n", vars["category"])
}

We see that the same way express accepts patterns, Gorilla mux allows us to use the same patterns. It can get a little complicated than what I’ve described, but I hope you get the idea.

Gorilla mux also comes with helper functions that npm’s body parser helps us achieve. Go purist can however claim that these function can easily be written.

Middleware

Middleware are a great part of NodeJS servers.

They are functions that sit somewhere, and are run before the actual request is run. In NodeJS, this is a simple snippet for a middleware that retrieves a secret from the env and uses it to authenticate a user. When reading variables from NodeJS, dotenv is commonly used.

const express = require(&apos;express&apos;);
const app = express();

// add server_name middleware
function authenticate((req, res, next) => {
    const secret = process.ENV.SECRET;
    if (secret == "") {
        return res.send("secret not found");
    }

    if (!isAuthenticated(req, secret)) {
        return res.send("invalid authentication");
    }

    return next();
})

app.get(&apos;/&apos;, authenticate, (req, res) => {
  res.send(&apos;Hello from Express!&apos;);
})

app.listen(3000, err => {
  if (err) {
    return console.log(&apos;something bad happened&apos;, err);
  }

  console.log(`server is listening on 3000`);
})

Go takes a similar approach. Since all a middleware does is take in a request do, something with it and decide whether the request should proceed.

package main

import (
    "net/http"
    "os"
)

// our sample authenticate middleware
func Authenticate(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r _http.Request) {
        secret := os.Getenv(&apos;SECRET&apos;)
        if len(secret) == 0 {
            w.Write(_[_]byte("secret not found")
            return
        }

        if !isAuthenticated(r, secret) {
            w.Write(_[_]byte("invalid authentication"))
            return
        }
        next.ServeHTTP(w, r)
    }

func Hello(w http.ResponseWriter, r _http.Request) {
    w.Write([]byte("Hello World"))
}

func main() {
    http.HandleFunc("/", Authenticate(Hello)) // call the middeware by wrapping
    if err := http.ListenAndServe(":8080", nil); err != nil {
        panic(err)
    }
}

If you are a seasoned JavaScript developer, you've probably noticed functions are first class citizens in Go too.

We've just written a function that takes in a [http.HandlerFunc](https://golang.org/pkg/net/http/#HandlerFunc) which is a function type, and the function structure is type HandlerFunc func(ResponseWriter, *Request), just like the Handler we wrote.

The advantage is that the http.HandlerFunc type has a function ServeHTTP which takes in the response and the request pointer we passed, and executes the handle call.

Some people call this approach wrapping functions, but this is the general idea. Again, you can easily write your own middlewares, but there are a couple of libraries out there to help you like http://www.gorillatoolkit.org/pkg/handlers and https://github.com/urfave/negroni.

Consuming APIs

Most of the time, our servers usually depend on an external API to get some data it needs. Let's say for example we are getting users from Github.

This is the approach you would take in a NodeJS app.

You'll first install a http request module, such as axios. npm install axios

const axios = require(&apos;axios&apos;);
const url = &apos;https://api.github.com/users&apos;;

axios.get(url).then(res => {
    // do something with the response
}).catch(err => {
    // do something with the error
})

This piece of code can either be in your service, or anywhere you like.

In Go, however, we do not need any external dependencies, and the net/http package can help us with this scenario.

package main

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

func main() {
    URL := "https://api.github.com/users"
    res, err := http.Get(URL)
    if err != nil {
        log.Println(err)
    }
    defer res.Body.Close() // for garbage collection

    responseBodyBytes, err := ioutil.ReadAll(res.Body)
    if err != nil {
        log.Println(err)
    }

    fmt.Println(string(responseBodyBytes))
}

We use http.Get to make requests, and check errors. The response is usually in bytes, and we have to read it first, then convert it to string with strinb([]bytes).

You can add this to a main.go file and run go run main.go.

This code can also easily be converted to a func, and called repeatedly when needed.

Database Connections

NodeJS has various npm modules to help in database connections depending on the databases that you are using.

These libraries most of the time come with their own methods, that try to make it easier for you to work with them, and some even offer ORM like features.

Go however takes a different approach. The standard library provides an interface for working with databases called https://golang.org/pkg/database/sql/ which RDBMS package authors can use to implement drivers for various databases. Authors following the standard interface ensure greater interoperability for their packages.

Common database driver packages include:

But to save you a lot of hustle, especially with SQL databases, https://github.com/jmoiron/sqlx will boost your productivity. Not to be left out of the ORM game either, the Go community has written many excellent ORMs such as https://github.com/jinzhu/gorm.

Conclusion and Other Cool Stuff

Go's files must be written within packages, and this usually affects your file structure.

In JavaScript, you'll see a lot of require statements at the beginning of files, while in Golang, the first line is always a package name, which will then be used as an import path in a file where it's required import package_path/package_name

I hope you've gotten a gist of what it's like to write Go, and you'd like to get into action. Golang is really praised for it's concurrency and performance, and if you are building a large application, this would be a preferred choice.

Here are some other cool stuff I did not cover, but are worth mentioning.

  • Go comes with an built-in testing package, and a Command Line tool go test to help you write and run your tests. In addition to this, there’s also a coverage tool, talked about in an article titled The Cover Story, as well as a benchmarking tool, to see the performance of your code. How cool is that.
  • Go comes with a formating tool called go fmt, which allows yout to format and lint your code appropriately. Unlike JavaScript, we have a one-size-fits all formating standard which the community adheres to.
  • If you want to learn more, there’s an extensive wiki in the Golang project’s repo with books, articles, videos on almost anything in Golang.

I've been following this transitional journey for a while now, and would glady answer any questions you may have. Just leave a comment.

Happy Go-ing! ;-)

Learn More

The Complete Node.js Developer Course (2nd Edition)

Learn and Understand NodeJS

Node JS: Advanced Concepts

GraphQL: Learning GraphQL with Node.Js

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Go: The Complete Developer’s Guide (Golang)

Learn How To Code: Google’s Go (golang) Programming Language

Web Development w/ Google’s Go (golang) Programming Language

Build Realtime Apps | React Js, Golang & RethinkDB

Prerequisites


Follow @gangachris_

Fullstack Developer


View My 17 Posts

Suggest