Controllers
Redirects, Metadata, And Errors
Set response metadata, redirect safely, and return expected HTTP errors.
Redirect after changes
Use controller redirects from actions:
func (c *PostsController) Create(
_ http.ResponseWriter,
_ *http.Request,
) error {
post := c.posts.Create()
return c.RedirectTo("/posts/"+post.Param, http.StatusSeeOther)
}
RedirectTo writes the redirect response and skips automatic rendering.
Redirect back safely
Use RedirectBackOrTo when a form should return to the referring page:
return c.RedirectBackOrTo("/posts", http.StatusSeeOther)
GoLazy only uses same-host referrers. External referrers fall back to the path you provide.
Set status and headers
Response metadata can be set before rendering:
c.Status(http.StatusCreated)
c.Header().Set("Cache-Control", "no-store")
c.Set("post", post)
return nil
Status does not commit the response early, so the normal template render can
still run.
Return expected errors
Wrap expected failures with an HTTP status:
if !ok {
return lazycontroller.Error(
http.StatusNotFound,
fmt.Errorf("post %q not found", postID),
)
}
The framework error handler uses the status code and renders
app/views/app/error.html.tpl through the current layout. If the application
does not provide that view, lazyapp falls back to the framework's mobile
friendly default error view. Use ReturnFile or ServeErrorPage only when an
action deliberately wants to serve a static public status page.
Raw error details are passed to the template only in development/detail mode.
Production responses get the status code and status text without exposing
err.Error() or backtrace frames.
Add application backtraces
Use lazyerrors.New when application code wants an unexpected error to carry
the source location where it was returned:
post, err := c.posts.Get(postID)
if err != nil {
return lazyerrors.New("load post %q: %w", postID, err)
}
The formatted error is prefixed with the caller, such as
posts.PostsController.Show: load post "hello": not found. %w wrapping keeps
working with errors.Is and errors.As; multiple %w values expose
Unwrap() []error.
Inspect a recorded trace through a local interface:
type backtracer interface {
Backtrace() []lazyerrors.Frame
}
var traced backtracer
if errors.As(err, &traced) {
for _, frame := range traced.Backtrace() {
log.Println(frame)
}
}
Each frame exposes Function, File, and Line, and still implements
String() for compact log output. Recovered panics use the same backtrace
shape, so panic detail pages render through the same frame list.
Show detailed errors
When an application runs through lazy, the build uses the lazydev tag and
unexpected errors, lazyerrors backtraces, and recovered panic backtraces are
shown with detail in the rendered error view. Production builds hide details
unless the app opts in:
lazyapp.New(lazyapp.Config{
ForceDetailErrors: true,
})
The built-in detailed error view displays frame paths relative to the active Go
workspace or current directory when possible. Module-cache frames are shortened
to module-relative paths such as
golang.org/toolchain@version/src/net/http/server.go. In lazydev, clicking a
frame sends a fire-and-forget request to /_golazy/open-editor; GoLazy starts
$EDITOR with the recorded absolute file path and line. VS Code-style editors
receive -g file:line. Terminal editors such as vim, nvim, and nano are
opened in a new terminal when GoLazy can discover one from Terminal.app on
macOS, $TERMINAL, Linux default terminal commands, parent-process terminal
names, or common terminal executables.
Unexpected errors are also logged to stderr so production responses can stay brief without losing the operational error.