Put Vue.js and Go together! Setup Web App in 5 mins
Tags: go, vue, webdev, tutorial
Published: 2020-10-09 10:50:48

Hello there! I'm Benno, a blue bear falls in love with coding :blue_heart:. 

The first time I met **Go** was about a year ago. I was looking for a self-hosted Git service for my team. I tried GitLab in the first place, but things didn't go smoothly. I had no root permission in the Linux server, and there was no Docker installed. So I could not even start the first step :sob:. When I almost gave up, Gitea just went inside my eyes. It's incredible that I could start a Git service with simply:

```
./gitea
```

That's the WOW moment I decided to learn Go and how to build this kind of stunning application :sparkles:.

So today I am going to show you how to put your frontend and backend codes into a single executable.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/odquaqhql9j4zykuhw7h.gif)


Let's get started!

# Install Tools

It's assumed that Go, NPM and Vue CLI are already installed in your workspace. If not, you can refer to below tutorials to get it installed:

- [Install Go](https://golang.org/doc/install)
- [Install npm](https://www.npmjs.com/get-npm)
- [Install Vue CLI](https://cli.vuejs.org/guide/installation.html)

If you are ready, run below command to get [go-bindata](https://github.com/go-bindata/go-bindata):

```

go get -u github.com/go-bindata/go-bindata/...

```

[go-bindata](https://github.com/go-bindata/go-bindata) is a command-line tool converts any file into Go source code. We are going to use it to package frontend codes.

# Prepare Working Directory

Now, create a working directory:

```bash

mkdir example-goweb
cd example-goweb

```

Initialize the Go project with:

```

go mod init example-goweb

```

I'm going to use [Gin](https://github.com/gin-gonic/gin) as the web framework, but in fact you can choose any framework, e.g. [Chi](https://github.com/go-chi/chi) if you like. Run:


```
go get -u github.com/gin-gonic/gin
```


After that, create a Vue App with **Vue CLI**:


```
vue create -n web
```


You could just follow the default setting for quick starting. A new folder `web/` will be created with Vue App source codes. You can run below commands to see the web application:

```
cd web
npm run serve
```

It should start the website at [http://localhost:8080](http://localhost:8080), which looks like:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ii5q97mzrmltjzjltvy1.png)

# Build Frontend

To build frontend, you could simply run `npm run build` under `web/` folder. However, we could do it better with `go generate`.

Create a new file `web/web.go` with following codes:

```go
package web

//go:generate npm run build
//go:generate go-bindata -fs -o web_gen.go -pkg web -prefix dist/ ./dist/...

```

`//go:generate` is a special comment to tell Go executing scripts when run `go generate`. You could get more information [here](https://blog.golang.org/generate).

Now let's run:

```
go generate ./web
```

It's exactly the same as running:

```
cd web
npm run build
go-bindata -fs -o web_gen.go -pkg web -prefix dist/ ./dist/...
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vt7o2jjpgc5v0bndb516.gif)

`go-bindata` will convert all files under `web/dist/` into Go source code `web_gen.go`, which could be used later.

# Go Coding!

We are almost there, the remaining works are:

1. Serve Static Files
2. Create `main()` Function

To Serve static files with HTTP, create a package `routers`:

```
mkdir routers
touch routers/routers.go
```

Open `routers.go` and add codes:

```go
package routers

import (
	"net/http"

	"example-goweb/web"

	"github.com/gin-gonic/gin"
)

// HandleIndex return HTML
func HandleIndex() gin.HandlerFunc {
	return func(c *gin.Context) {
		html := web.MustAsset("index.html")
		c.Data(200, "text/html; charset=UTF-8", html)
	}
}

// Register routes
func Register(e *gin.Engine) {
	h := gin.WrapH(http.FileServer(web.AssetFile()))
	e.GET("/favicon.ico", h)
	e.GET("/js/*filepath", h)
	e.GET("/css/*filepath", h)
	e.GET("/img/*filepath", h)
	e.GET("/fonts/*filepath", h)
	e.NoRoute(HandleIndex())
}

```

Let me explain what's going on. We create a `gin.HandlerFunc` to serve files with HTTP:

```go
h := gin.WrapH(http.FileServer(web.AssetFile()))
```

`web.AssetFile()` is a function from `web/web_gen.go`, it creates a `http.FileSystem` and looks up static files inside `web_gen.go`.

Then we tell `gin` to handle every `GET` static files request:

```go
e.GET("/favicon.ico", h)
e.GET("/js/*filepath", h)
e.GET("/css/*filepath", h)
e.GET("/img/*filepath", h)
e.GET("/fonts/*filepath", h)
```

When user requests for a static file, like JavaScript, `gin` will handle the request and `http.FileSystem` will return the file.
The last line in `Register` function tells `gin` to return `index.html` if there is no matching route.

```go
func HandleIndex() gin.HandlerFunc {
	return func(c *gin.Context) {
		html := web.MustAsset("index.html")
		c.Data(200, "text/html; charset=UTF-8", html)
	}
}
```

Finally, let's create a `main.go`:

```go
package main

import (
	"example-goweb/routers"

	"github.com/gin-gonic/gin"
)

func main() {
	e := gin.Default()
	routers.Register(e)
	e.Run(":8080")
}
```

The `main()` creates a `gin` engine to register routes and start the HTTP server with **:8080** port.

Your web server is ready to go! Run below command and visit [http://localhost:8080](http://localhost:8080) to see the result:

```
go run main.go
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ctac1qdhpoibilv3ru16.gif)

You can build executable and start server with:

```
go build -o goweb ./
./goweb
```

Here is what your working folder should look like in the end! :tada:

---

```
.
└── example-goweb/
    ├── routers/
    │   └── routers.go
    ├── web/
    │   ├── dist/
    │   │   ├── css
    │   │   ├── favicon.ico
    │   │   ├── img
    │   │   ├── index.html
    │   │   └── js
    │   ├── README.md
    │   ├── babel.config.js
    │   ├── node_modules
    │   ├── package-lock.json
    │   ├── package.json
    │   ├── public
    │   ├── src
    │   ├── web.go
    │   └── web_gen.go
    ├── go.mod
    ├── go.sum
    └── main.go
```

---

# Conclusion

Packaging your web application into a single executable makes deployment extremely easy. Many real-world applications adopt this solution, for example:

- [Gitea](https://github.com/go-gitea/gitea)
- [Drone](https://github.com/drone/drone)
- [Traefik](https://github.com/traefik/traefik)

If you like this approach and would like to have more examples, check my open source project **covergates**:

[https://github.com/covergates/covergates](https://github.com/covergates/covergates)

It's a self-hosted coverage report alternative to [Code Climate](https://codeclimate.com/), [Codecov](https://codecov.io/) or [Coveralls](https://coveralls.io/).
You can have your own coverage report service simply with:

```
wget https://github.com/covergates/covergates/releases/download/v0.2.1/covergates-v0.2.1-linux-amd64.tar.gz
tar -zxvf covergates-v0.2.1-linux-amd64.tar.gz
./covergates-server
```

In fact, there is a cool trick to change base URL for Vue router on the fly. You can find it in the source code or give me a heart :heart: to let me know you are interested in it. I'll have another tutorial next time. See Ya! :laughing: