Start Here
Getting Started
Run the sample application and follow a request from app startup to an HTML response.
Prerequisites
GoLazy currently targets Go 1.26 or later. Confirm your installation:
go version
Begin with the sample_app
repository, or start smaller with the single-file app.
Install the CLI and create an app
Install the GoLazy command:
go install github.com/golazy/lazy@latest
Create a new application from the matching sample_app tag:
lazy new github.com/guillermo/my_app
The command clones the sample app template matching the CLI version, removes
its Git history, renames sample_app references to your module path, runs
go mod tidy, and verifies the generated application with go test ./....
The lazy command
lazy is a helper command, not the framework runtime. It exists to make common
development tasks shorter, such as creating a new application from the sample
template and running the application command from the module root.
You can use the full framework without using lazy. A GoLazy application is a
normal Go module with a normal cmd/app executable, and the framework is used
through ordinary Go imports such as golazy.dev/lazyapp,
golazy.dev/lazyroutes, and golazy.dev/lazycontroller.
The Go toolchain remains the source of truth:
go run ./cmd/app
go test ./...
go build ./cmd/app
The helper can also inspect the framework route table:
lazy routes
That command runs the application with the lazydev,printroutes build tags,
prints the routes, and exits before the HTTP server starts.
Run the application
From the sample application directory, either use the helper command:
lazy
or run the application directly:
go run ./cmd/app
The server listens on :8080 by default. Open
http://localhost:8080 in a browser.
Use ADDR to select another port or address:
ADDR=3000 go run ./cmd/app
ADDR=127.0.0.1:3000 go run ./cmd/app
Follow the startup path
The executable asks the application initializer for the complete handler:
app := appinit.App()
server := &http.Server{
Addr: listenAddr(),
Handler: app,
}
init/app.go wires the framework pieces:
func App() *lazyapp.App {
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Context: Context,
})
}
This order matters:
lazyapp.Newopens views and initializes the renderer.Contextinitializes application services.Drawregisters application routes on alazyroutes.Scope.lazydispatchinstalls router and public-file middleware.http.Serverserves the completed application handler.
Follow one request
For GET /posts, the resource route binds the posts controller constructor to
its Index action:
func Draw(router *lazyroutes.Scope) {
router.Resources(posts.New)
}
Resources derives /posts from PostsController, registers the implemented
REST actions, and creates a new controller for each request. The action loads
data into the controller and renders a view:
func (c *PostsController) Index(_ http.ResponseWriter, _ *http.Request) error {
c.Set("title", "Posts")
c.Set("posts", c.posts.List())
return c.Render("index")
}
Render("index") resolves:
app/views/posts/index.html.tpl
and composes it with:
app/views/layouts/app.html.tpl
Make a first change
Edit app/views/home/index.html.tpl, restart lazy or go run ./cmd/app, and
reload the home page. Templates are embedded at build time, so a running binary
does not read changed files from disk.
Restart the Go process after changing Go code, templates, embedded content, or public files.
Build for production
GoLazy applications build with normal Go commands. There is no framework build step, code generation pass, or special packager:
go build ./cmd/app
That command uses Go's default output name. In the sample application the
default name is app, which would collide with the existing app/ directory,
so production builds normally choose an explicit artifact path with Go's
standard -o flag:
go build -o bin/my_app ./cmd/app
The resulting binary is self-contained. Views, layouts, public files, and other embedded resources are compiled into the executable, so the deployed process does not need template files or a public directory beside it.
A minimal Dockerfile can copy only the binary into the runtime image:
FROM golang:1.26 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/app
FROM scratch
COPY --from=build /out/app /app
EXPOSE 8080
ENV ADDR=:8080
ENTRYPOINT ["/app"]
Verify the application
Run the complete test suite:
go test ./...
Before a release, also run:
go test -race ./...
go vet ./...
go build -o /tmp/sample-app ./cmd/app
Continue with Application Structure for a map of the repository.