An introduction to Go, by leveraging your favorite JS flavor.
Published: 2024-01-16 02:25:04

I'll assume you have a comprehensive understanding of JavaScript and frontend. I'll just focus on the Go code and to familiarize ourselves a bit with Go, we'll create a simple desktop app with [Wails](https://wails.io/) to track our subscriptions.

Simply put Wails is like the Tauri of the Go world, they both leverage the native web view of your operating system (WebView 2 in my case as I'm developing on windows) to build desktop apps, but while Tauri uses Rust as the backend logic language, Wails uses Go.


![Default Wails boilerplate app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5cl6vbbjhtsm9c7khnd.png)

## Prerequisites 

- Make sure you have [Go](https://go.dev/) (1.18+) on your system 
- Make sure you have [Node & NPM](https://nodejs.org/en) on your system 

Wails mentions in their docs how you can [download, configure and verify](https://wails.io/docs/gettingstarted/installation) these tools on your system.

## Install the Wails CLI

Installing the Wails CLI via go install will allow us to call the Wails cli from anywhere in our system

`go install github.com/wailsapp/wails/v2/cmd/wails@latest`

## Generate a Wails app

Generate a wails app with React and TypeScript, via the CLI
(or with your favorite flavor. Svelte, Vue, Lit, Preact or Vanilla JS)

`wails init -n myproject -t react-ts`

or without `TypeScript` if you prefer

`wails init -n myproject -t react`

You can run your application in development mode by running `wails dev ` from your project directory.


Typically `package main` and usually `main.go` are the entry point of any go program and we'll rarely need to touch this file, but this will create and run our app.
`main.go`

```go
package main

import (
	"embed"
	"github.com/wailsapp/wails/v2"
	"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)

//go:embed all:frontend/dist
var assets embed.FS

func main() {
	// Create an instance of the app structure
	app := NewApp()

	// Create application with options
	err := wails.Run(&options.App{
		Title:  "Simple Suscription Tracker",
		Width:  1080,
		Height: 710,
		AssetServer: &assetserver.Options{
			Assets: assets,
		},
		BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
		OnStartup:        app.startup,
		Bind: []interface{}{
			app,
		},
	})

	if err != nil {
		println("Error:", err.Error())
	}
}

```
`OnStartup` calls a method called startup on our app instance, which is found in `app.go`

We define a method by using parens after the `func `keyword and before the function name, passing in a the thing itself with a pointer type, which references the struct in memory. 

In this case, the startup method is already defined for us

```go
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
	a.ctx = ctx
}
```

![Final version of complete introduction demo wails app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6nscb9tguuwo8hanfgj6.png)

**//NOTE: CONNECTING TO A DB FROM A DESTOP APP IS NOT BEST PRACTICES AND NOT RECOMMENED, THIS IS JUST FOR LEARNING PURPOSES, TO LEARN GORM.**

In this project, I'm using [gorm](https://gorm.io/docs/) to connect to a [railway](https://railway.app/) Postgres Database to persist subscriptions, so I'll create a method to connect and create the database tables if they don't already exist.

```go
func (a *App) connectToDB() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading .env file")
		return
	}
	dsn := os.Getenv("DATABASE_URL")
	conn, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})

	if err != nil {
		log.Fatal("Error connecting to the db")
		return
	}

	db = conn
	log.Println("Connected To DB")
}

func (a *App) createTables() {
	db.AutoMigrate(&Subscription{})
}
```

The `connectToDB` func needs a string to connect to the DB an we don't want to expose it in our code, so we'll utilize [gotdotenv](https://github.com/joho/godotenv) to load the env variable from our .env and then add it to our `.gitignore`. 

Install godotenv and gorm

`go get github.com/joho/godotenv`
`go get -u gorm.io/gorm`

And if like me, you're using Postgres, you'll need to install the driver for gorm

`gorm.io/driver/postgres`

You'll notice the connection is assigned to a variable `db`, that is outside the scope of the function. I defined it globally, so we can use it from other functions and in go, to initialize a variable without a value, we use the `var `keyword and give it a type.

```go
var db *gorm.DB
``` 

The type here is a pointer to the gorm connection.

Now we can call these two methods on our app on startup by adding them to the startup `func`.


```go
func (a *App) startup(ctx context.Context) {
	a.ctx = ctx
	a.connectToDB()
	a.createTables()
}
```

Go has a concept of a `struct`, a struct defines a custom data structure

```go
type Subscription struct {
	Id        string    `gorm:"primaryKey" json:"Id"`
	Name      string    `json:"Name"`
	Price     string    `json:"Price"`
	CreatedAt time.Time `json:"CreatedAt"` 
// Automatically managed by GORM for creation time
	UpdatedAt time.Time `json:"UpdatedAt"` 
// Automatically managed by GORM for update time
}
```

The code inside of the backticks in the struct are known as field tags, they are optional and tell certain libraries how to treat each struct field and gorm has [many](https://gorm.io/docs/models.html#Fields-Tags).

`gorm:"primaryKey"` is pretty self explanatory, but it specifies the column as primary key, gorm automatically  uses fields named `CreatedAt `and `UpdatedAt `to automatically track the creation and update times of records.

GORM simplifies database interactions by mapping Go structs to database tables and `AutoMigrate `runs auto migration for given models.

Here, we migrate our subscription struct and pass in an empty `struct`.

```go
func (a *App) createTables() {
	db.AutoMigrate(&Subscription{})
}
```

Now we have our DB setup, we'll define some functions to be called from our frontend. You'll notice these functions are capitalized here too, that's to ensure they can be exported from that package.

```go
func (a *App) GetAllRecords() (string, error) {
	var subscriptions []Subscription
	result := db.Find(&subscriptions)

	if result.Error != nil {
		log.Fatalln("Error executing query:", result.Error)
	}

	data, err := json.Marshal(subscriptions)
	if err != nil {
		log.Fatalf("Failed to marshal data: %v", err)
	}

	return string(data), nil
}

func (a *App) CreateRecord(name string, price string) {

	v4, err := uuid.NewRandom()
	if err != nil {
		log.Println("Error creating uuid", err)
		return
	}

	v4Str := v4.String()
	log.Println(name)
	log.Println(price)

	subscription := Subscription{Id: v4Str, Name: name, Price: price}
	result := db.Create(&subscription)

	log.Printf(fmt.Sprint(result.RowsAffected))
}

func (a *App) GenUUID() (string, error) {
	v4, err := uuid.NewRandom()

	if err != nil {
		return "", err
	}

	return v4.String(), nil
}

func (a *App) DeleteById(id string) {
	db.Delete(&Subscription{}, "Id = ?", id)
}
```

Now we can import and call our functions from our frontend. 
`frontend\src\App.tsx`

```js
import { CreateRecord, GetAllRecords } from "../wailsjs/go/main/App";
```

If you'd like me to cover what I'm doing in the TS and cover more of the go code, let me know.

The github repo can be found [here](https://github.com/MVAodhan/simple-subscriptions)