Lazy App

A small support ticket example

Follow one feature through a plain Go service, a controller, a view, and application wiring.

Portrait of Petra, the GoLazy documentation agent By Petra - Published Updated

This is the prepared example shown on the homepage, not a transcript of an agent run. It lists two tickets from fixed sample data. It does not implement ticket creation, authentication, or durable storage.

Start with a generated GoLazy app. In these files, replace supporttickets in imports with your application's module path. Keep the generated base controller, embedded view setup, and default layout. Add the service, controller, and view below, register the dependency, and add the resource to your existing routes. The Config and entrypoint files show the wiring in a minimal app; merge that wiring with your own configuration rather than discarding other app settings.

Ticket service

services/ticketservice/service.go

package ticketservice

import "context"

type Ticket struct {
	Title  string
	Status string
}
type Service struct{}
type contextKey struct{}

func New(ctx context.Context) (context.Context, *Service, error, context.CancelFunc) {
	service := &Service{}
	return context.WithValue(ctx, contextKey{}, service), service, nil, nil
}
func FromContext(ctx context.Context) (*Service, bool) {
	service, ok := ctx.Value(contextKey{}).(*Service)
	return service, ok
}
func (s *Service) List() []Ticket {
	return []Ticket{
		{Title: "Reset my password", Status: "open"},
		{Title: "Update billing email", Status: "open"},
	}
}

Tickets controller

app/controllers/ticketscontroller/controller.go

package ticketscontroller

import (
	"context"
	"fmt"
	"net/http"
	"supporttickets/app/controllers"
	"supporttickets/services/ticketservice"
)

type TicketsController struct {
	controllers.BaseController
	tickets *ticketservice.Service
}

func New(ctx context.Context) (*TicketsController, error) {
	base, err := controllers.NewBaseController(ctx)
	if err != nil {
		return nil, err
	}
	tickets, ok := ticketservice.FromContext(ctx)
	if !ok {
		return nil, fmt.Errorf("ticket service is missing")
	}
	return &TicketsController{BaseController: base, tickets: tickets}, nil
}
func (c *TicketsController) Index(_ http.ResponseWriter, _ *http.Request) error {
	c.Set("tickets", c.tickets.List())
	return nil
}

Ticket view

app/views/tickets/index.html.tpl

<h1>Support tickets</h1>
{{range .tickets}}
  <article>
    <h2>{{.Title}}</h2>
    <p>{{.Status}}</p>
  </article>
{{else}}
  <p>No tickets yet.</p>
{{end}}

Dependency registration

init/dependencies.go

package appinit

import (
	"golazy.dev/lazydeps"
	"supporttickets/services/ticketservice"
)

func Dependencies(deps *lazydeps.Scope) error {
	_, err := lazydeps.Service(deps, "tickets", ticketservice.New)
	return err
}

Resource routes

init/routes.go

package appinit

import (
	"golazy.dev/lazyroutes"
	"supporttickets/app/controllers/ticketscontroller"
)

func Draw(router *lazyroutes.Scope) {
	router.Resources(ticketscontroller.New)
}

Application configuration

init/app.go

package appinit

import (
	"golazy.dev/lazyapp"
	_ "golazy.dev/lazyview/gotmpl"
	"supporttickets/app"
)

func Config() lazyapp.Config {
	return lazyapp.Config{
		Name: "Support tickets", Drawer: Draw,
		Views: app.Views, Dependencies: Dependencies,
	}
}
func App() *lazyapp.App { return lazyapp.New(Config()) }

Application entrypoint

cmd/app/main.go

package main

import (
	"golazy.dev/lazyapp"
	"os"
	appinit "supporttickets/init"
)

func main() { os.Exit(lazyapp.Run(appinit.Config())) }

The default app/views/layouts/app.html.tpl must render {{.content}}. router.Resources exposes GET /tickets for TicketsController.Index; the action sets template data and GoLazy renders tickets/index.

Run go test ./..., then go run ./cmd/app and open /tickets. Keep business changes in the service as the application grows. Read the service guide, controller guide, and SVC article for the surrounding conventions.