Codementor Events

Getting Started With Angular and Go — Setting Up A Boilerplate Project.

Published Dec 27, 2017Last updated Jun 25, 2018
Getting Started With Angular and Go — Setting Up A Boilerplate Project.

I have seen a lot of people on social media groups asking if there is a boilerplate project present for Angular/React + Go. As both an Angular and Go developer myself, I decided to create one.

The project that I created can be found here. In this post, we will look into how I created it so you can understand how it works and customize it if you want. The current boilerplate offers a very basic setup. I’ll be working on improving it soon.

Please note that this boilerplate doesn’t consider serving the Angular app from server. This setup treats the Go server and Angular app as two separate entities and only helps in running them together or managing their dependencies together. With this setup, you can run each of them separately as well if you need, without any changes.

As mentioned in the GitHub Repo’s README, as of now, the boilerplate is only good for development purposes. You will still have to manually build clients and servers separately while also modifying some settings (you’ll see these later) manually. I am working on automating everything but it is going to take some time. Anyways, let’s dive into it.

Client Setup (Angular)

Prerequisites:

  1. You have Angular CLI and NPM installed.

First, generate a new Angular project in the src folder in your GOPATH or any subfolder of the src folder. You can do so by opening a terminal at that folder and running:

ng new your-awesome-project

This will generate a new Angular project and will install all of the dependencies for you. That’s it for the Client side for now.

Server Setup (Go)

Prerequisites:

  1. You have Go installed.
  2. You have GOHOME and GOPATH properly setup.

First, run the following in the terminal to install gin.

go get github.com/codegangsta/gin

Gin is a live reload utility for Go servers that automatically builds and runs the server on changes to .go files. It is very similar to Webpack’s Dev Server — the difference being it doesn’t automatically refresh the browser and only rebuilds the server. You will have to refresh the browser manually if you update your server code.

Next, we are going to install two more Go packages: https://github.com/gorilla/mux to handle routing and https://github.com/rs/cors for handling Cross Origin Requests.

CORS stands for Cross Origin Resource Sharing. Since our client and server will be running on different ports and eventually on different domains, allowing CORS requests from one domain to another has to be explicitly configured.

For me, the best place to put all of my server code is in a folder called server, inside of the src folder in the project generated by Angular CLI. Now the three folders that we will be working with further are app, environments, and server.

Let’s first set up a simple Go server and forget about CORS for a moment. For a simple server, create a file called main.go. The code in this file will look something like this:

package main

import (
  "github.com/gorilla/mux"
  "net/http"
  "os"
  "log"
  "your-awesome-project/src/server/utils"
)

func main() {
  r := mux.NewRouter()

  r.HandleFunc("/hello-world", helloWorld)

  http.Handle("/", r)

  srv := &http.Server{
    Handler: r,
    Addr:    ":" + os.Getenv("PORT"),
  }

  log.Fatal(srv.ListenAndServe())
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
  var data = struct {
    Title string `json:"title"`
  }{
    Title: "Golang + Angular Starter Kit",
  }

  jsonBytes, err := utils.StructToJSON(data); if err != nil {
    fmt.Print(err)
  }

  w.Header().Set("Content-Type", "application/json")
  w.Write(jsonBytes)
  return
}

Wait, why are we fetching the Addr from the OS’s environment variable to run our server in localhost? This is a necessary requirement for Gin to work properly. Gin automatically sets the port (default is 3000) which you can change while running Gin.

You might also notice that I have also imported a local package called utils. In this package’s folder, there is a file called json.go. That is going to contain functions that will handle all JSON related actions, like encoding and decoding. For the purpose of this post, we will only need JSON encoding. Here is what the code in json.go looks like:

package utils

import (
  "encoding/json"
  "bytes"
)

func StructToJSON (data interface{}) ([]byte, error) {
  buf := new(bytes.Buffer)

  if err := json.NewEncoder(buf).Encode(data); err != nil {
    return nil, err
  }

  return buf.Bytes(), nil
}

This function essentially creates a new buffer, which is passed as an argument to json.NewEncoder() method for writing data.

A Buffer is a variable-sized buffer of bytes with Read and Write methods. The zero value for Buffer is an empty buffer ready to use.

The encoded JSON data is written to this new buffer and can be read either as []byte or as string using the methods present on type buffer . In this case, we are returning the encoded data as []byte because we need the encoded data in that format to be able to write it in our response header.

If you are confused about why the type of argument to function StructToJSON is of type interface{} or also known as “empty interface,” it’s the same as type any in TypeScript.

Now we need to implement a way for both our Client and Server to run together and watch for changes, without having to do this process manually. I found a simple way to do this.

First, create a file called serve.sh in the root of your project folder (in this case, your-awesome-project folder). The contents of this file are as follows:

#!/bin/sh

ng serve &
gin --port 4201 --path . --build ./src/server/ --i --all &

wait

Okay, so what’s going on? If you are a noob in bash scripting, let me tell you what this does (I actually looked around until I found this — I am no good in bash scripting either — Thanks Google!). The first line is similar to <!DOCTYPE HTML> in the sense that it also defines the type of this file. Moving on, the next two lines are simply what you would run from the terminal.

ng serve starts the Webpack Dev Server for Angular, and gin --port 4201 --path . --build ./src/server/ --i spins up the live reload utility for Go server on port 4201, looks for file changes in the src directory, builds the binary from ./src/server directory, and reloads the server immediately after file changes. For more information on flags used by Gin, visit the docs here.

You can queue as many commands as you would like to run by simply adding & character as a suffix to them. In the end, the line containing wait is where the commands queued will execute in parallel (Yes, in parallel, that’s why we needed bash scripting) and will wait for all of them to finish, which won’t happen until we hit CTRL+C in this case.

Another advantage (or disadvantage perhaps?) of running the servers this way is that the console output for both Webpack and Go server will be printed in the same console from which the script is executed.

Before the script can be executed though, we need to change its permissions and make it executable. Simply run chmod 775 ./serve.sh in the root folder (where the sh file is present) to make it executable.

As a last step, to run it via NPM instead, simply edit the package.json file. Under the scripts object, edit the value of start key and make it “./serve.sh.” Once this is done, simply running npm start will spin up both Angular’s and Go’s development servers simultaneously.

Now that we have the code in place for starting our servers, let’s connect them and test it out. First, let’s set the base URL of our server in environments so that we can access it from anywhere in our Angular app. The update environment.ts file will look like this:

export const environment = {
  production: false,
  serverUrl: 'http://localhost:4201'
};

Now, let’s create a service that will make request to our Go server.

ng g s hello-world

A simple implementation will look something like this:

import { Injectable } from '@angular/core';
import {Http} from '@angular/http';
import {environment} from '../environments/environment';
import 'rxjs/add/operator/map';

@Injectable()
export class HelloWorldService {

  constructor(private http: Http) { }

  getTitle() {
    return this.http.get(`${environment.serverUrl}/hello-world`)
      .map(response => response.json());
  }

}

In our app.component.ts, we can utilize this service to fetch the title from our server. The modified file will look something like this:

import {Component, OnInit} from '@angular/core';
import {HelloWorldService} from './hello-world.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  title;

  constructor(private hw: HelloWorldService) {}

  ngOnInit() {
    this.hw.getTitle()
      .subscribe(data => this.title = data.title);

    console.log(this.title);
  }

}

Before running the app, don’t forget to add our new service to the list of providers, as well as import HttpModule.

Time to test our app. Hit npm start from the terminal to spin up the servers. Once both go and webpack servers are running, visit http://localhost:4200 in your browser. The title will be empty. Any guess why? CORS! Here’s what you will see in the console:

Failed to load http://localhost:4201/hello-world: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.

This is the issue I was talking about earlier. Let’s implement the cors package that we downloaded earlier and fix this issue. The updated main.go file will look like this:

package main

import (
  "github.com/gorilla/mux"
  "net/http"
  "os"
  "log"
  "angular-go-boilerplate/src/server/utils"
  "fmt"
  "github.com/rs/cors"
)

func main() {
  r := mux.NewRouter()

  r.HandleFunc("/hello-world", helloWorld)

  // Solves Cross Origin Access Issue
  c := cors.New(cors.Options{
    AllowedOrigins: []string{"http://localhost:4200"},
  })
  handler := c.Handler(r)

  srv := &http.Server{
    Handler: handler,
    Addr:    ":" + os.Getenv("PORT"),
  }

  log.Fatal(srv.ListenAndServe())
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
  var data = struct {
    Title string `json:"title"`
  }{
    Title: "Golang + Angular Starter Kit",
  }

  jsonBytes, err := utils.StructToJson(data); if err != nil {
    fmt.Print(err)
  }

  w.Header().Set("Content-Type", "application/json")
  w.Write(jsonBytes)
  return
}

In cors.Options struct, you can set all of the domains that you want to allow cross origin requests from. In our case, it will be just one domain and that is http://localhost:4200. That’s all we need. You will also realize that the Go server is automatically reloaded and you just need to refresh the browser.

Note: Remember I mentioned earlier that for production builds, you will have to manually change a few settings. This is one of those settings. I am working on automating everything and will update the repo as well as this post soon.

Voila! We did it. Our starter project for working on Go + Angular projects is now complete.

Managing Dependencies

Now, let’s look into how to make it easier for us to share code without messing up dependencies. All you need to do is in the package.json file, under scripts: add a new entry having the keyinstall-dependencies with value npm install && go get github.com/codegangsta/gin && go get ./src/server/… . Now you and other contributors can simply install all dependencies for both Angular and Go by running npm run install-dependencies.

That’s all for today. I really hope that some of you found this helpful for your own projects. If you have any suggestions to improve the boilerplate or this post, do let me know.

If you did find this useful, do leave a clap and share it with someone who needs it. Follow me for updates regarding this project and a few others that I plan on working on.

Cheers!

Discover and read more posts from Anshul Sanghi
get started
post comments2Replies
Sakar SR
7 years ago

Good article to show how to use Angular and Go together.

steven kauyedauty
7 years ago

Thank you for this!