Skip to content

romsar/gonertia

Repository files navigation

Gonertia

gonertia

Gonertia is a well-tested and zero-dependency Inertia.js server-side adapter for Golang. Visit inertiajs.com to learn more.

Audit Workflow Go Report Card Go Reference MIT license

Introduction

Inertia allows you to create fully client-side rendered, single-page apps, without the complexity that comes with modern SPAs. It does this by leveraging existing server-side patterns that you already love.

This package based on the official Laravel adapter for Inertia.js: inertiajs/inertia-laravel.

Roadmap

  • Tests
  • Helpers for testing
  • Helpers for validation errors
  • SSR

Installation

Install using go get command:

go get github.com/romsar/gonertia

Usage

Basic example

Initialize Gonertia in your main.go:

package main

import (
    "log"
    "http"
    "time"
    
    inertia "github.com/romsar/gonertia"
)

func main() {
    i, err := inertia.New("./ui/templates/root.html")
    if err != nil {
        log.Fatal(err)
    }

    // Now create your HTTP server.
    // Gonertia works well with standard http handlers,
    // but you are free to use some frameworks like Gorilla Mux or Chi.
    mux := http.NewServeMux()

    mux.Handle("/home", i.Middleware(homeHandler(i)))
}

func homeHandler(i *inertia.Inertia) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        err := i.Render(w, r, "Home/Index", inertia.Props{
            "some": "data",
        })
		
        if err != nil {
           handleServerErr(w, err)
        }
    }

    return http.HandlerFunc(fn)
}

Create root.html template:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>title</title>
    {{ .inertiaHead }}
</head>

<body>
{{ .inertia }}
<script src="/static/js/app.js"></script>
</body>
</html>

More examples

Load root template using embed

import "embed"

//go:embed templates
var templateFS embed.FS

// ...

i, err := inertia.New(
    /* ... */
    inertia.WithTemplateFS(templateFS),
)

Set asset version (learn more)

i, err := inertia.New(
    /* ... */
    inertia.WithVersion("some-version"), // by any string
    inertia.WithAssetURL("/static/js/1f0f8sc6.js"), // by asset url
    inertia.WithManifestFile("./ui/manifest.json"), // by manifest file
)

Replace standard JSON marshall function

import jsoniter "github.com/json-iterator/go"

// ...

i, err := inertia.New(
    /* ... */, 
    inertia.WithMarshalJSON(jsoniter.Marshal),
)

Use your logger

i, err := inertia.New(
    /* ... */
    inertia.WithLogger(somelogger.New()),
    // or inertia.WithoutLogger(),
)

Set custom container id

i, err := inertia.New(
    /* ... */
    inertia.WithContainerID("inertia"),
)

Closure and lazy props (learn more)

props := inertia.Props{
    "regular": "prop",
    "closure": func () (any, error) { return "prop", nil },
    "lazy": inertia.LazyProp(func () (any, error) {
        return "prop", nil
    },
}

i.Render(w, r, "Some/Page", props)

Redirects (learn more)

func homeHandler(i *inertia.Inertia) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        i.Location(w, r, "/some-url")
    }

    return http.HandlerFunc(fn)
}

NOTES: If response is empty - user will be redirected to the previous url.

To redirect back, you can use Back helper:

i.Back(w, r)

Share template data (learn more)

i.ShareTemplateData("title", "Home page")
<title>{{ .title }}</title>

Share template func

i.ShareTemplateFunc("trim", strings.Trim)
<title>{{ trim "foo bar" }}</title>

Pass template data via context (in middleware)

ctx := i.WithTemplateData(r.Context(), "title", "Home page")

// pass it to the next middleware or inertia.Render function using r.WithContext(ctx).

Share prop globally (learn more)

i.ShareProp("name", "Roman")

Pass props via context (in middleware)

ctx := i.WithProp(r.Context(), "name", "Roman")
// or i.WithProps(r.Context(), inertia.Props{"name": "Roman"})

// pass it to the next middleware or inertia.Render function using r.WithContext(ctx).

Validation errors (learn more)

ctx := i.WithValidationError(r.Context(), "some_field", "some error")
// or i.WithValidationErrors(r.Context(), inertia.ValidationErrors{"some_field": "some error"})

// pass it to the next middleware or inertia.Render function using r.WithContext(ctx).

Testing

Of course, this package provides convenient interfaces for testing!

func TestHomepage(t *testing.T) {
    body := ... // get an HTML or JSON using httptest package or real HTTP request. 
	
    // ...
	
    assertable := inertia.Assert(t, body) // io.Reader body
    // OR
    assertable := inertia.AssertFromBytes(t, body) // []byte body
    // OR
    assertable := inertia.AssertFromString(t, body) // string body
	
    // now you can do assertions using assertable.Assert[...] methods:
    assertable.AssertComponent("Foo/Bar")
    assertable.AssertVersion("foo bar")
    assertable.AssertURL("https://example.com")
    assertable.AssertProps(inertia.Props{"foo": "bar"})

    // or work with the data yourself:
    assertable.Component // Foo/Bar
    assertable.Version // foo bar
    assertable.URL // https://example.com
    assertable.Props // inertia.Props{"foo": "bar"}
}

Credits

This package is based on inertiajs/inertia-laravel and uses some parts of petaki/inertia-go.

License

Gonertia is released under the MIT License.