GoWebsite-Boilerplate

command module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2023 License: Unlicense Imports: 15 Imported by: 0

README

Go Website Boilerplate Template

This template is a starting point for my Go web projects including my prefered technologies described below.
While it bends to my preferences, it is designed to be adaptable, expandable and easy to set up and modify to your needs.

As of v0.1.0, it is also my first Go project, so it may not be perfect and major overhauls may happen.
Contributions are welcome, but please keep in mind that this is a template, not a framework.

Features

  • Go webserver with Gin
  • HTMX with Go templates
  • HTMX WebSocket support
  • API handlers
  • PostgreSQL with sqlx
  • Tailwind CSS (compiled on startup)
  • Database migration system
  • Dockerfile with multi-stage build
  • Demo of all features

Template Setup

After cloning the template, to finalize the setup, follow these quick steps:

  1. Clone your repo: Replace [YourUsername] and [YourRepoName] with your GitHub username and repository name.

    git clone https://github.com/[YourUsername]/[YourRepoName].git
    cd [YourRepoName]
    
  2. Run the setup script: This script essentially renames the project to your repository name.

    python repo-template-setup.py
    
  3. Commit and push:

    git add -A
    git commit -m "Finalize setup"
    git push
    

That's it! Your repo is now ready to use.

Requirements

Try it out

To try the demo, you need to set up a database and .env, as the demo relies on it.

cp .env.example .env

Then edit the .env file to match your database settings. Your .env file must be set up, since it will use the same variables for the app as setting up the database in docker.

docker compose -f docker-compose.dev-db.yml up

You can now build the application or run the release version:

go build
./GoWebsite-Boilerplate

View the demo at http://localhost:8080

Development

Change the layout

The base templateof the application is defined in the templates/layouts/default_base.html file.
You can change this as needed, but mind the imports and templating to maintain all functionality.

Creating a page
Create a new html file in the templates directory
Add the following to any page template:
{{ template "default_base.html" . }}
{{ define "content" }}
Your page content should go here
{{ end }}
Create a handler for the page in ./handlers with this structure:
var db *sqlx.DB

// HomePageHandler Implements types.HandlerRegistrar interface
type HomePageHandler struct{}

// Initialize is called before the handler is registered
func (h *HomePageHandler) Initialize(initContext *types.HandlerInitContext) {
    db = initContext.DB
}

// Implements PageRouteRegistrar interface
func (h *HomePageHandler) Handler(engine *gin.Engine) {
    engine.GET("/", h.get)
}

func (h *HomePageHandler) get(c *gin.Context) {
    // Set SEO meta data
    meta := &page.PageMetaData{
        Title:       "Demo Home Page",
        Description: "This is a demo home page showing off the boilerplate.",
    }

    // Any additional data required by the page
    data := &map[string]interface{}{
        "Counter": CounterData{
            Value: 0,
            Color: "#fff",
        },
    }

    // Turn it into structured data
    structuredData := page.StructurePageData(&data, meta)

    // Render page
    c.HTML(http.StatusOK, "home_page.html", structuredData)
}

You can register multiple related handlers in one file. For more details see the Gin documentation

Register the handler in ./handlers/registration.go:

Add the handler to the RouteHandlers slice:

&yourmodule.SomePageHandler{},
Creating an HTMX component

The instructions are the same as for creating a page, but you want to add the route in the relevant page or system's handler file.
Additionally, htmx component templates should not include {{ template "default_base.html" . }}.

You can still use page.StructurePageData(data, nil) without defining meta for between page and component, if the page loads the component as well.

API Handlers

They are registered in ./handlers/registration.go alongside page handlers.
API handlers should return JSON data. Included in the package is a structured API response outputting responses like so:

{
    "success": true,
    "data": {
        "someData": "Some data"
    }
}

or for errors:

{
    "success": false,
    "error": "Error message"
}

You can generate this data using common.ApiResponseFactory.Ok(data) and common.ApiResponseFactory.Error(err) respectively.

An API handler will generally look like this:

var db *sqlx.DB

// HomeApiHandler Implements types.HandlerRegistrar interface
type HomeApiHandler struct{}

// HomePageData is the response for the time call
type HomePageData struct {
    Time string `json:"time"`
}

// Initialize is called before the handler is registered
func (h *HomeApiHandler) Initialize(initContext *types.HandlerInitContext) {
    db = initContext.DB
}

func (h *HomeApiHandler) Handler(engine *gin.Engine) {
    engine.GET("/api/home/get-server-time", h.get)
}

func (h *HomeApiHandler) get(c *gin.Context) {
    timeStr := time.Now().Format("2006-01-02 15:04:05")
    resp := common.ApiResponseFactory.Ok(
        &HomePageData{Time: timeStr})

    // Render page
    c.JSON(http.StatusOK, resp)
}

Static and dynamic assets

These are files that are served directly by the webserver in slightly different ways.

Static Assets
  • Repo path: ./assets/static/
  • Web path: /
  • Embedded: Yes
  • Ideal for: Small, commonly used files

Static assets are embedded in the executable, this means they can be served faster, but they cannot be changed without recompiling the application. It also means it's not suitable for large files.
It's ideal for small files like favicons, logos, javascript libraries, etc.
They each get their own handler registered directly at /.

Dynamic Assets
  • Repo path: ./assets/dynamic/
  • Web path: /dynamic/
  • Embedded: No
  • Ideal for: Large files, files that change often

Dynamic assets are not embedded in the executable, this means they can be changed without recompiling the application, but they are served slightly slower.
It's ideal for large files like images, videos, etc. assuming they are not hosted on a CDN. They do not have direct handlers, but instead get served as fallback before the 404 handler is called.

We also use this to serve compiled Tailwind files, since they are generated on application startup.
You can change this behavior in by setting up your own pre-compile postcss call and disabling config.MinifyCss in config/config.go.

Default Template Definitions

These ensure that all pages have the required blocks and that the layout is loaded correctly.
You can add additional blocks to the layout as needed in config/template_definitions.go.
The default definitions are:

  • content: Each page expects a content block to be defined. HTMX components do not use this block.
  • scripts: Optional, at the bottom of the page. Can be used to add additional scripts to the page. <script> tags are required.
  • head: Optional, will be loaded in <head> of the page.
  • page_title: Will be used as the page title. Defaults to website name.

Utility Functions

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL