DEV Community

Cover image for Put Vue.js and Go together! Setup Web App in 5 mins
Benno
Benno

Posted on

Put Vue.js and Go together! Setup Web App in 5 mins

Hello there! I'm Benno, a blue bear falls in love with coding πŸ’™.

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 😭. When I almost gave up, Gitea just went inside my eyes. It's incredible that I could start a Git service with simply:

./gitea
Enter fullscreen mode Exit fullscreen mode

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

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

Alt Text

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:

If you are ready, run below command to get go-bindata:


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

Enter fullscreen mode Exit fullscreen mode

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:


mkdir example-goweb
cd example-goweb

Enter fullscreen mode Exit fullscreen mode

Initialize the Go project with:


go mod init example-goweb

Enter fullscreen mode Exit fullscreen mode

I'm going to use Gin as the web framework, but in fact you can choose any framework, e.g. Chi if you like. Run:

go get -u github.com/gin-gonic/gin
Enter fullscreen mode Exit fullscreen mode

After that, create a Vue App with Vue CLI:

vue create -n web
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

It should start the website at http://localhost:8080, which looks like:

Alt Text

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:

package web

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

Enter fullscreen mode Exit fullscreen mode

//go:generate is a special comment to tell Go executing scripts when run go generate. You could get more information here.

Now let's run:

go generate ./web
Enter fullscreen mode Exit fullscreen mode

It's exactly the same as running:

cd web
npm run build
go-bindata -fs -o web_gen.go -pkg web -prefix dist/ ./dist/...
Enter fullscreen mode Exit fullscreen mode

Alt Text

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
Enter fullscreen mode Exit fullscreen mode

Open routers.go and add codes:

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())
}

Enter fullscreen mode Exit fullscreen mode

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

h := gin.WrapH(http.FileServer(web.AssetFile()))
Enter fullscreen mode Exit fullscreen mode

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:

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)
Enter fullscreen mode Exit fullscreen mode

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.

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

Finally, let's create a main.go:

package main

import (
    "example-goweb/routers"

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

func main() {
    e := gin.Default()
    routers.Register(e)
    e.Run(":8080")
}
Enter fullscreen mode Exit fullscreen mode

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 to see the result:

go run main.go
Enter fullscreen mode Exit fullscreen mode

Alt Text

You can build executable and start server with:

go build -o goweb ./
./goweb
Enter fullscreen mode Exit fullscreen mode

Here is what your working folder should look like in the end! πŸŽ‰


.
└── 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
Enter fullscreen mode Exit fullscreen mode

Conclusion

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

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

https://github.com/covergates/covergates

It's a self-hosted coverage report alternative to Code Climate, Codecov or Coveralls.
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
Enter fullscreen mode Exit fullscreen mode

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 ❀️ to let me know you are interested in it. I'll have another tutorial next time. See Ya! πŸ˜†

Top comments (3)

Collapse
 
nferdazel profile image
Fredianto

Hi, Benno! Nice article. I got question about how do you make the folder tree structure like that? Thank you!

Collapse
 
blueworrybear profile image
Benno

Hi Fredianto! You could use Linux command tree to display folder in the tree structure. Or you can try tree.nathanfriend.io, it takes files list and turns it into tree!

Collapse
 
nferdazel profile image
Fredianto

Wow! I just know that there's such a tool like that online! Thank you, Benno!