Gonertia

repository·3.x·Indexed 42 days ago

https://github.com/romsar/gonertia

A zero-dependency Golang adapter for Inertia.js v3, enabling the creation of single-page applications using server-side routing and controller patterns. It features support for SSR, Vite integration with HMR, asset versioning, and advanced prop patterns including deferred, optional, and merging props.

Tokens
11.9K
Snippets
49
Records
56
Agent score
54%

What's inside Gonertia

  1. Overview of Gonertia

    3.x
    Gonertia is a zero-dependency Inertia.js server-side adapter for Golang. It is designed to allow developers to build single-page applications (SPAs) using server-side patterns, similar to how the official Laravel adapter works. It supports Inertia 3.0 and features like SSR (Server-Side Rendering) and helpers for validation errors.
  2. Use Content Security Policy (CSP) with Vite

    3.x

    To secure your application with CSP, use the CSPMiddleware provided by the Vite integration. This applies nonces to all generated asset tags.

    1. Wrap your handler: handler := app.CSPMiddleware()(app.Middleware(mux)).
    2. In your template, use {{ viteAssetsWithNonce .csp_nonce "path" }} to include the nonce.
    3. Customize the policy using app.CSPMiddleware(inertia.WithCSPPolicy("..."), inertia.WithCSPNonceGenerator(fn)).
    handler := app.CSPMiddleware()(app.Middleware(mux))
    
    // Template usage:
    // {{ viteAssetsWithNonce .csp_nonce "app.tsx" }}
  3. Integrate with Vite

    3.x

    Gonertia provides built-in Vite integration to handle hot module replacement (HMR) and production asset bundling automatically.

    Basic Setup

    Wrap your Inertia instance with inertia.NewWithVite(i).

    Configuration

    Use inertia.NewWithVite(i, ...) with these options:

    • inertia.WithHotFile(path): Path to the Vite hot reload file.
    • inertia.WithBuildManifest(path): Path to the build manifest.
    • inertia.WithFallbackManifest(path): Fallback manifest path.
    • inertia.WithBuildDir(dir): Build output directory.
    • inertia.WithHotReloadPort(port): Hot reload server port.

    Template Helpers

    • {{ .inertiaHead }}: Required in <head>.
    • {{ viteRefresh }}: HMR setup for frameworks like Vue/Preact.
    • {{ vite "path" }}: Resolves asset URLs for dev vs production.
    • {{ viteAssets }}: Outputs all required assets (use with WithEntryPoints).
    • {{ viteAssetsWithNonce .csp_nonce "path" }}: Outputs assets with a CSP nonce.
    i, _ := inertia.NewFromFile("resources/views/app.html")
    app, _ := inertia.NewWithVite(i, 
        inertia.WithBuildManifest("public/build/manifest.json"),
        inertia.WithHotFile("custom/hot"),
    )
    
    // In your HTML template:
    // <script type="module" src="{{ vite "resources/js/app.js" }}"></script>
  4. Find community adapters for Gonertia

    3.x
    If you need an adapter for a specific Go framework not included in the core package, you can check community-maintained options. One available adapter is petaki/inertia-go. For a complete list of available community adapters, visit the official Inertia.js website.
  5. Initialize Gonertia and Render Pages

    3.x

    To use Gonertia, initialize an *inertia.Inertia instance using one of the New constructors (from string, file, file system, reader, or bytes). You can then wrap your HTTP handlers with i.Middleware() to integrate with standard Go http.Handler or routers like Chi/Gorilla Mux. To render a component, use i.Render(w, r, componentName, props) within your handler.

    package main
    
    import (
        "log"
        "net/http"
    
        inertia "github.com/romsar/gonertia/v3"
    )
    
    func main() {
        // Initialize from a string or file
        i, err := inertia.New(rootHTMLString)
        if err != nil {
            log.Fatal(err)
        }
    
        mux := http.NewServeMux()
    
        // Wrap handler with middleware
        mux.Handle("/home", i.Middleware(homeHandler(i)))
        http.ListenAndServe(":8080", mux)
    }
    
    func homeHandler(i *inertia.Inertia) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Render the "Home/Index" component with props
            err := i.Render(w, r, "Home/Index", inertia.Props{
                "some": "data",
            })
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
            }
        })
    }
  6. How the Inertia Middleware manages request lifecycle

    3.x

    The Middleware works by wrapping the standard http.ResponseWriter with an inertiaResponseWrapper. This is necessary because the standard http.ResponseWriter does not allow inspecting the status code or the response body after they have been written.

    The Lifecycle:

    1. Intercept: The middleware checks if the request is an Inertia request.
    2. Wrap: It creates an inertiaResponseWrapper which buffers the response in memory (since Inertia responses are JSON and typically small).
    3. Execute: It calls the next handler in the chain using the wrapper instead of the original writer.
    4. Post-Process: After the handler finishes, the middleware inspects the wrapper's status code and buffer. It performs automatic redirects (for empty responses or version mismatches) or status code adjustments (302 to 303 for non-GET methods).
    5. Flush: Finally, it copies the buffered headers, status code, and body from the wrapper back to the original http.ResponseWriter to complete the HTTP response.
  7. Use Vite template helpers in HTML templates

    3.x

    Once initialized, ViteInstance registers several helper functions with your Inertia instance that can be used directly in your Go templates (e.g., html/template).

    • vite(asset): Resolves an asset path (returns the URL or the path from the manifest).
    • viteReactRefresh(): Returns the <script> tags required for React Fast Refresh (only works in HMR mode).
    • viteRefresh(): Returns the <script> tag for the Vite client (only works in HMR mode).
    • viteAssets: Generates all necessary asset tags.
    • viteAssetsWithNonce(nonce): Generates all asset tags with a provided CSP nonce.
    <!-- Example usage in a Go template -->
    <head>
        {{ viteReactRefresh }}
        {{ viteRefresh }}
        {{ viteAssets }}
    </head>
    
    <!-- Manual asset resolution -->
    <script src="{{ vite "main.js" }}"></script>
  8. Configure Inertia using functional options

    3.x

    The gonertia package uses the functional options pattern to configure an Inertia instance. You can pass multiple Option functions to the initialization process to customize behavior such as versioning, logging, SSR, and encryption. Each Option is a function with the signature func(i *Inertia) error.

    // Example of applying options during initialization
    // (Assuming an Inertia constructor exists that accepts ...Option)
    inertia := gonertia.NewInertia(
        gonertia.WithVersion("1.0.0"),
        gonertia.WithLogger(myLogger),
        gonertia.WithSSR("http://localhost:13714"),
    )
  9. Enable Server Side Rendering (SSR)

    3.x

    To enable SSR, pass the inertia.WithSSR() option during initialization. By default, it attempts to connect to http://127.0.0.1:13714. You can specify a custom URL using inertia.WithSSR(url).

    i, err := inertia.New(
        rootHTMLString,
        inertia.WithSSR(), // default: http://127.0.0.1:13714
        inertia.WithSSR("http://127.0.0.1:1234"),
    )
  10. Custom Prop resolution interfaces

    3.x

    If you need complex logic to resolve a prop, implement one of the following interfaces. The Render method will automatically detect and call them:

    • Proper: Returns a value via Prop() any.
    • ProperWithContext: Returns a value via PropWithContext(ctx context.Context) any.
    • TryProper: Returns a value and an error via TryProp() (any, error).
    • TryProperWithContext: Returns a value and an error via TryPropWithContext(ctx context.Context) (any, error).
    type UserResolver struct {
        ID int
    }
    
    func (u UserResolver) PropWithContext(ctx context.Context) any {
        // Fetch user from DB using context
        return user
    }
    
    // Usage in Render
    i.Render(w, r, "User/Profile", Props{"user": UserResolver{ID: 1}})
  11. Configure Asset Versioning

    3.x

    To ensure clients receive updated assets, set an asset version during initialization. You can provide a static string or a path to a manifest file (which Gonertia will use to calculate a checksum).

    Options:

    • inertia.WithVersion(string): Sets a manual version string.
    • inertia.WithVersionFromFile(path): Sets version based on file checksum.
    • inertia.WithVersionFromFileFS(fs, path): Sets version based on file checksum from an fs.FS.
    i, err := inertia.New(
        rootHTMLString,
        inertia.WithVersion("some-version"),
        inertia.WithVersionFromFile("./public/build/manifest.json"),
        inertia.WithVersionFromFileFS(embedFS, "./public/build/manifest.json"),
    )