Messages¶
Flash message support for post-redirect-get feedback.
Package: github.com/oliverandrich/burrow/contrib/messages
Depends on: session
Setup¶
Register the messages app after session (it depends on session for storage):
The messages app installs middleware that reads flash messages from the session into the request context and clears them, giving each message a single-request lifetime.
Adding Messages¶
Use the convenience helpers inside any handler — typically just before a redirect:
import "github.com/oliverandrich/burrow/contrib/messages"
func (a *App) Create(w http.ResponseWriter, r *http.Request) error {
// ... create resource ...
if err := messages.AddSuccess(w, r, "Note created."); err != nil {
return err
}
http.Redirect(w, r, "/notes", http.StatusSeeOther)
return nil
}
Available helpers:
| Helper | Level |
|---|---|
messages.AddInfo(w, r, text) |
info |
messages.AddSuccess(w, r, text) |
success |
messages.AddWarning(w, r, text) |
warning |
messages.AddError(w, r, text) |
error |
For full control, use messages.Add(w, r, level, text) with any messages.Level.
Reading Messages¶
Each Message has two fields:
Level— one ofmessages.Info,messages.Success,messages.Warning,messages.ErrorText— the message string
In Go Code¶
msgs := messages.Get(r.Context())
for _, msg := range msgs {
fmt.Printf("%s: %s\n", msg.Level, msg.Text)
}
In Layout Templates¶
Messages are available in layout templates via the messages template function (provided by the messages contrib app via HasRequestFuncMap). The four level values (info, success, warning, error) map straight to whatever CSS conventions your layout uses. The example/notes layout uses Tailwind utility classes via a per-level helper define:
{{ range messages -}}
<div class="rounded-md border px-3 py-2 text-sm {{ template "app/alert_classes" .Level }}" role="alert">
{{ .Text }}
</div>
{{ end -}}
See example/notes/internal/app/templates/app/layout.html for the full app/alert_classes define (one Tailwind class set per level).
Custom Rendering¶
If you use a different CSS framework, call messages.Get(ctx) directly and map levels to your own classes:
func toastClass(level messages.Level) string {
switch level {
case messages.Success: return "toast-success"
case messages.Warning: return "toast-warning"
case messages.Error: return "toast-error"
default: return "toast-info"
}
}
How It Works¶
The middleware creates a mutable, request-scoped store. Add() writes to both the store and the session cookie. Get() reads from the store and clears the session cookie to prevent double-display.
Redirect flow (post-redirect-get)¶
- Handler calls
messages.Add(w, r, level, text)— writes to store + session cookie - Redirect sends the browser to the target page (cookie included)
- Middleware on the next request seeds a new store from the session and clears the session
- Template calls
messages.Get(ctx)— reads from the store, message appears exactly once
Same-request flow (HTMX partial)¶
- Handler calls
messages.Add(w, r, level, text)— writes to store + session cookie - Template calls
messages.Get(ctx)— reads from the store, clears the session cookie - Browser receives the response with the cleared cookie — no message persists for the next request
Testing¶
Use session.Inject to set up session state, then call messages.Add and read back with messages.Get:
func TestFlashMessage(t *testing.T) {
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/", nil)
req = session.Inject(req, map[string]any{})
err := messages.AddSuccess(rec, req, "Saved")
require.NoError(t, err)
values := session.GetValues(req)
msgs := values["_messages"].([]messages.Message)
assert.Equal(t, messages.Success, msgs[0].Level)
assert.Equal(t, "Saved", msgs[0].Text)
}
For template tests that need messages in context, use messages.Inject:
ctx := messages.Inject(context.Background(), []messages.Message{
{Level: messages.Success, Text: "Done"},
})
// Render template with ctx
Interfaces Implemented¶
| Interface | Description |
|---|---|
burrow.App |
Required: Name() |
burrow.HasMiddleware |
Flash message middleware (read from session, inject into context, clear) |
burrow.HasRequestFuncMap |
Provides the messages template function used in layouts |
burrow.HasDependencies |
Declares dependency on session |