Go

About Go for Web development

Using Go for web development is not that common. But my personal experience is that Go outperforms Wordpress, Drupal, PHP and Angular several times. And combining Javascript in the browser and Go on the server makes it a perfect choice for web development:

Fast

Go is a compiled language, which means that it is much faster than interpreted languages like JavaScript. This is important for web applications, as users expect fast loading times.

Efficient

Go is a very efficient language, which means that it uses less memory and CPU resources than many other languages. This is important for server-side applications, as it can help to reduce costs and reduce environment impact.

Secure

Go is a very secure language, which means that it is less vulnerable to security threats than interpreted languages. This is important for web applications, as users expect their data to be safe.

Powerful

Go is a very powerful language, which means that it can be used to build complex web applications.

Easy to use

Go is a rather easy language to learn and use, even for beginners.

Simple to deploy

Deploying an web application is about first compile Go into "machine code". Then transfer the compiled Go files together with other files (HTML, CSS, JavaScript etc) to the web server. Then start the Go built in web server. No app engines. Only Nginx (which is my prefered "proxy web server").

The function that creates and populate the form

Creates and add data in one step. Writing to innerHTML means less flickering.

// write form to buffer an send to html body
func get_card(w http.ResponseWriter, module string, mode string, val string) {
    page := module + "_" + mode
    data := json2map4id(module, val)
    var buf bytes.Buffer
    tpl.ExecuteTemplate(&buf, page, data) // write to buffer
    fmt.Fprint(w, buf.String()) // send to body
}
The form function in the context

Here is the entire main.go that manages forms and translations.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"strings"

	"github.com/leonelquinteros/gotext"
)

var tpl *template.Template
var mainmenu string
var submenu string
var translations map[string]*gotext.Po
var usr_lang string

//one time init
func init() {
	translations = make(map[string]*gotext.Po)

	languages := []string{"en", "es", "de", "sv"}
	for _, lang := range languages {
		path := "./public/locale/" + lang + ".po"
		langPo := gotext.NewPo()
		langPo.ParseFile(path)
		translations[lang] = langPo
	}

	tpl = template.Must(template.New("").Funcs(template.FuncMap{
		"trans": trans,
	}).ParseGlob("./public/tmpl/*/*.html"))

	load_main()
	load_sub()

	http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./public/img"))))
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("./public/css"))))
	http.Handle("/icn/", http.StripPrefix("/icn/", http.FileServer(http.Dir("./public/icn"))))
	http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./public/js"))))
	http.Handle("/misc/", http.StripPrefix("/misc/", http.FileServer(http.Dir("./public/misc"))))
}

// start webserver and mux
func main() {
	http.HandleFunc("/", endpoint)
	http.ListenAndServe(":9098", nil)
}

//function that is called from the templates
func trans(key string) string {
	path := "./public/locale/" + usr_lang + ".po"
	//	translationsMux.Lock()
	po, ok := translations[usr_lang]
	if !ok {
		po = gotext.NewPo()
		po.ParseFile(path)
		translations[usr_lang] = po
	}
	//	translationsMux.Unlock()
	return po.Get(key)
}

// fetch language setting from cookie
func usr_getlang(r *http.Request) string {
	lang, _ := r.Cookie("lang")
	if lang != nil {
		usr_lang = lang.Value
	} else {
		usr_lang = "en"
	}
	return usr_lang
}

// dynamic endpoints
func endpoint(w http.ResponseWriter, r *http.Request) {
	module, mode, val := getpath(r.URL.Path)
	usr_getlang(r)
	fmt.Println(module + mode + val)
	var page string
	switch module {
	case "robots.txt":
		http.ServeFile(w, r, "public/misc/robots.txt")
	case "sitemap.xml":
		http.ServeFile(w, r, "public/misc/sitemap.xml")
	case "favicon.ico", "favicon-32x32.png", "favicon-16x16.png":
		return
	case "main":
		get_main(w)
		return
	case "sub":
		get_sub(w, val)
		return
	case "":
		module = "home"
	}

	switch mode {
	case "edit", "new", "view", "find", "goto", "status", "timer":
		get_card(w, module, mode, val)
	default:
		page = module + ".html"
		set_header(w)
		data := json2map(module, mode, val)
		tpl.ExecuteTemplate(w, page, data)
	}
}

func set_header(w http.ResponseWriter) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "*")
}

// write form to buffer an send to html body
func get_card(w http.ResponseWriter, module string, mode string, val string) {
	page := module + "_" + mode
	fmt.Println(page)
	data := json2map4id(module, val)
	var buf bytes.Buffer
	tpl.ExecuteTemplate(&buf, page, data)
	fmt.Fprint(w, buf.String())
}

// navigation from cache
func get_main(w http.ResponseWriter) {
	var menu []map[string]interface{}
	if err := json.Unmarshal([]byte(mainmenu), &menu); err != nil {
		fmt.Println(err.Error())
	}
	var buf bytes.Buffer
	tpl.ExecuteTemplate(&buf, "main.html", menu)
	fmt.Fprint(w, buf.String())
}

// navigation from cache
func get_sub(w http.ResponseWriter, submenu string) {
	submenu = filter_sub(submenu)
	var menu []map[string]interface{}
	if err := json.Unmarshal([]byte(submenu), &menu); err != nil {
		fmt.Println(err.Error())
	}
	var buf bytes.Buffer
	tpl.ExecuteTemplate(&buf, "sub.html", menu)
	fmt.Fprint(w, buf.String())
}

// split url
func getpath(path string) (module, mode, val string) {
	parts := strings.Split(path, "/")
	switch len(parts) {
	case 4:
		val = parts[3]
		fallthrough
	case 3:
		mode = parts[2]
		fallthrough
	case 2:
		module = parts[1]
	}
	return // Named return values are used, so just return here
}

// API call for all records
func json2map(module string, mode string, val string) interface{} {
	// call the API and get body
	url := "https://api3.go4webdev.org/" + module + "/all"
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err.Error())
	}
	defer resp.Body.Close()

	// json to map
	var result interface{}
	err = json.NewDecoder(resp.Body).Decode(&result)
	if err != nil {
		fmt.Println(err.Error())
	}

	return (result)
}

//API call for one id
func json2map4id(module string, val string) interface{} {
	// call the API and get body
	url := "https://api3.go4webdev.org/" + module + "/id/" + val
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err.Error())
	}
	defer resp.Body.Close()

	// json to map
	var result interface{}
	err = json.NewDecoder(resp.Body).Decode(&result)
	if err != nil {
		fmt.Println(err.Error())
	}
	return (result)
}