frontend-go

Go Reference

SPA (Single Page Application) style web application frontend helper for go server. This package works with Next.js, Vue.js, SvelteKit, Solid.js.

Development Mode

It runs dev server behind go server. It works as reverse proxy. You can get all features that the web framework dev server provides (hot module replacement and so on).

Development Mode

Production Mode

It gets prebuilt assets (HTML, JS, CSS) from go’s embed. Basically, all SPA needs to fallback to index.html when there is not have assets.

Release Mode

Integration

frontend-go assumes the following structure. Important points are the following:

  • There is frontend project folder named frontend.
  • There is release.go at the parent folder of frontend folder.
  • Add frontend-go’s handler to your web server.

awesome-your-web-app
├── LICENSE
├── README.md
├── cmd
│   └── server
│      └── main.go    # Add frontend-go's handler
├── go.mod
├── go.sum
├── api.go
├── release.go        # You should add this
└── frontend          # frontend project (Next.js, Vue.js, SvelteKit, Solid.js)
    └── package.json

release.go has the following code.

//go:build release

package webapp

// ↓this folder is decided by your frontend framework

//go:embed frontend/build/*
var asset embed.FS

init() {
    frontend.SetFrontAsset(asset, frontend.Opt{
        FrameworkType: frontend.NextJS, // select: NextJS, VueJS, SvelteKit, SolidJS
    })
}

Use with net/http:

package main

func handler() http.Handler {
    mux := http.NewServeMux()
    mux.Handle("/api", YourAPIHandler)
    mux.Handle("/", frontend.MustNewSPAHandler())
    return mux
}

Use with chi:

import (
    "github.com/go-chi/chi/v5"
)

func handler() http.Handler  {
    r := chi.NewRouter()
    r.Post("/api", YourAPIHandler)
    r.NotFound(frontend.MustNewHandlerFunc())
    return r
}

# Development mode
$ go run main.go

# Production build
$ go build -tags release

Web Frameworks Specific Instructions

Next.js 12

frontend-go assumes Next.js’s static generation result (that does’n need Node.js/bun to run).

To start project with Next.js, init project like this:

$ go mod init yourapp
$ mkdir -p cmd/yourapp
$ npx [email protected] --ts frontend

To enable static generation, add unoptimized flag to next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  experimental: { // Add here
    images: {
      unoptimized: true,
    }
  }
}

You should build frontend project by using the following commands.

$ npm run build
$ npm exec next export

The go:embed directive comment should be the following by default:

//go:embed frontend/out/*
//go:embed frontend/out/_next/static/*/*
//go:embed frontend/out/_next/static/chunks/pages/*.js
var asset embed.FS

init() {
    frontend.SetFrontAsset(asset, frontend.Opt{
        FrameworkType: frontend.NextJS,
    })
}

Vue.js

To start project with Vue.js, init project like this:

$ go mod init yourapp
$ mkdir -p cmd/yourapp
$ vue create frontend

You should build frontend project by using the following commands.

$ npm run build

The go:embed directive comment should be the following by default:

//go:embed frontend/dist/*
var asset embed.FS

init() {
    frontend.SetFrontAsset(asset, frontend.Opt{
        FrameworkType: frontend.VueJS,
    })
}

SvelteKit

Default SvelteKit provides frontend program that requires Node.js to run. To work with this module, you should configure the front end project with static site mode.

$ go mod init yourapp
$ mkdir -p cmd/server
$ npm create [email protected] frontend

To modify front end site, add static-adaptor.

$ npm install @sveltejs/adapter-static

import adapter from '@sveltejs/adapter-static'; // Modify here

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: preprocess(),

  kit: {
    adapter: adapter({       // Modify here for static HTML/JS generation
      fallback: 'index.html'
    }),
    prerender: {             // Modify here for SPA mode
      default: false
    },
    trailingSlash: 'always'  // Modify here
  }
};

export default config;

You should build frontend project by using the following commands.

$ npm run build

The go:embed directive comment should be the following by default:

//go:embed frontend/build/*
var asset embed.FS

init() {
    frontend.SetFrontAsset(asset, frontend.Opt{
        FrameworkType: frontend.SvelteKit,
    })
}

Solid.js

$ go mod init yourapp
$ mkdir -p cmd/server
$ npx degit solidjs/templates/ts frontend

You should build frontend project by using the following commands.

$ npm run build

The go:embed directive comment should be the following by default:

//go:embed frontend/dist/*
var asset embed.FS

init() {
    frontend.SetFrontAsset(asset, frontend.Opt{
        FrameworkType: frontend.SolidJS,
    })
}

Configuration

frontend.SetFrontAsset() (for production), frontend.SetOption() (other usage) accept option that modifies package’s behavior.

If you put frontend project at frontend folder and doesn’t change npm scripts, you don’t have to modify configuration.

//go:build release

:

//go:embed frontend/build/*
var asset embed.FS

handler := frontend.SetFrontAsset(assets, frontend.Opt{
    FrontEndFolder: "frontend",              // Frontend application folder that contains package.json. default value is "frontend"
    ProjectType:    frontend.AutoDetect,     // NextJS, SvelteKit, VueJS, SolidJS is available
    SkipRunningDevServer:     false,         // Skip running dev server even if development mode
    DistFolder:     "",                      // Specify dist folder instead of auto detect
    Port:           0,                       // Specify port instead of auto detect
    DevelopmentCommand: "npm run dev",       // Specify dev server command instead of auto detect
    FallbackPath:       string               // Specify fallback file path. Default is "index.html"
})

For development mode, this package tries to configure package as much as possible, including framework type.

If you want to set option for development mode, add the following file and set option:

//go:build !release

package webapp

init() {
    frontend.SetOption(frontend.Opt{
        SkipRunningDevServer: true,
    })
}

Credits

Yoshiki Shibukawa

License

Apache 2

GitHub

View Github