DEV Community

loading...

How to build Web App with Go + gin-gonic + Vue

Masayoshi Mizutani
Updated on ・3 min read

This article describes how to build and develop Web application with Go + gin-gonic (Web Application Framework in Go) + Vue.

(Edited) Sample code repository: https://github.com/m-mizutani/web-app-go

Steps

Configure webpack

At first, install yarn packages

$ yarn init
$ yarn add -D @babel/cli @babel/core @babel/preset-env babel-loader webpack webpack-cli webpack-dev-server html-webpack-plugin vue-loader vue-template-compiler css-loader vue-style-loader sass-loader
$ yarn add babel-polyfill vue  node-sass axios

After that, create webpack.config.js file.

const path = require('path');
const VueLoaderPlugin = require("vue-loader/lib/plugin");

module.exports = {
  mode: "development",
  entry: ["babel-polyfill", path.resolve("src", "js", "index.js")],
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "static/js/"),
    publicPath: "/js"
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader"
      },
      {
        test: /\.js$/,
        loader: "babel-loader"
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          // Creates `style` nodes from JS strings
          'vue-style-loader',
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Sass to CSS
          'sass-loader',
        ],
      }
    ]
  },
  resolve: {
    extensions: [".js", "json", "jsx", "vue"],
    alias: {
      vue$: "vue/dist/vue.esm.js"
    }
  },
  devServer: {
    contentBase: "static",
    proxy: {
      "/api": "http://localhost:9080"
    }
  },
  plugins: [new VueLoaderPlugin()]
};

Additoinaly, put following setting into package.json.

  "scripts": {
    "start": "webpack-dev-server",
    "build": "webpack --optimize-minimize"
  },

Add web assets

Required following files:

  • src/css/main.scss
  • src/js/index.js
  • src/js/app.vue
  • static/index.html

main.scss

body {
  font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
}

index.js

import '../css/main.scss'

import _ from 'lodash';
import "babel-polyfill";
import Vue from "vue";

import App from "./app.vue";

new Vue({
    el: "#app",
    render: h => h(App)
});

app.vue

<template>
  <div>
    <div>MyApp</div>
    <button v-on:click="showMessage">This is test</button>
    <div>{{message}}</div>
  </div>
</template>
<script>
import axios from "axios";
const appData = {
  message: ""
};
export default {
  data() {
    return appData;
  },
  methods: {
    showMessage: showMessage
  }
};
function showMessage() {
  axios.get("/api/v1/hello").then(res => {
    console.log(res);
    appData.message = res.data.message;
  });
}
</script>
<style>
</style>

index.html

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>MyApp</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/js/bundle.js"></script>
  </body>
</html>

Add go module

Initialize go module.

$ go mod init github.com/m-mizutani/web-app-go

After that, add main.go.

package main

import (
    "fmt"
    "os"

    "github.com/gin-contrib/static"
    "github.com/gin-gonic/gin"
    "github.com/sirupsen/logrus"
)

var logger = logrus.New()

var logLevelMap = map[string]logrus.Level{
    "trace": logrus.TraceLevel,
    "debug": logrus.DebugLevel,
    "info":  logrus.InfoLevel,
    "warn":  logrus.WarnLevel,
    "error": logrus.ErrorLevel,
}

type arguments struct {
    LogLevel       string
    BindAddress    string
    BindPort       int
    StaticContents string
}

func runServer(args arguments) error {
    level, ok := logLevelMap[args.LogLevel]
    if !ok {
        return fmt.Errorf("Invalid log level: %s", args.LogLevel)
    }
    logger.SetLevel(level)
    logger.SetFormatter(&logrus.JSONFormatter{})

    logger.WithFields(logrus.Fields{
        "args": args,
    }).Info("Given options")

    r := gin.Default()

    r.Use(static.Serve("/", static.LocalFile(args.StaticContents, false)))
    r.GET("/api/v1/hello", func(c *gin.Context) {
        c.String(200, `{"message":"hello, hello, hello"}`)
    })

    if err := r.Run(fmt.Sprintf("%s:%d", args.BindAddress, args.BindPort)); err != nil {
        return err
    }

    return nil
}

func main() {
    args := arguments{
        LogLevel: "info",
        BindAddress: "0.0.0.0",
        BindPort: 9080,
        StaticContents: "./static",
        }

    if err := runServer(args); err != nil {
        logger.WithError(err).Fatal("Server exits with error")
    }
}

Development

Run go server

$ go run .

air command with .air.conf like following is recommended for hot reloading.

[build]
include_ext = ["go", "tpl", "tmpl", "html"]
exclude_dir = ["src", "tmp", "node_modules"]
delay = 1000 # ms
stop_on_error = true
log = "air_errors.log"

And run air

$ air -c .air.conf

Start webpack development server

$ npm run start

Open browser

Open http://localhost:8080 to confirm Web UI.

Deploy

$ npm run build
$ go build -o sercer
$ cp -r server static /path/to/server

Discussion (2)

Collapse
fabiansilva profile image
Fabian Silva

thanks for your post, I'm learning go and it seems good to start with.

Can please upload the code to some repo? thanks.

Collapse
mizutani profile image
Masayoshi Mizutani Author

Thank you for your comment. I added sample code repository URL (github.com/m-mizutani/web-app-go) to head of the post.