Development
Cheatsheet
Short GoLazy snippets for common how-can-I changes, written as sample-app deltas.
This cheatsheet answers "how can I?" with small sample-app changes. Each snippet is intentionally narrow: it shows the files you would add or edit from the generated sample app, and the code comments carry the local context.
The snippets are not a replacement for the guides. Use them to start the change, then follow the linked guide when the app needs the full design, tradeoffs, tests, or deployment notes.
Routes
// init/routes.go
package appinit
import (
"golazy.dev/lazyroutes"
homecontroller "sample_app/app/controllers/home_controller"
postcontroller "sample_app/app/controllers/post_controller"
)
func Draw(router *lazyroutes.Scope) {
router.Resources(homecontroller.New, func(home *lazyroutes.Resource) {
home.Singular("home")
home.Plural("home")
home.Path("")
})
router.Get("/posts/{post_id}", postcontroller.New, (*postcontroller.PostsController).Show)
router.Post("/posts", postcontroller.New, (*postcontroller.PostsController).Create)
}
Controllers
// app/controllers/post_controller/postcontroller.go
package postcontroller
import (
"context"
"fmt"
"sample_app/app/controllers"
"sample_app/services/postservice"
)
type PostsController struct {
controllers.BaseController
posts postservice.Service
}
func New(ctx context.Context) (*PostsController, error) {
base, err := controllers.NewBaseController(ctx)
if err != nil {
return nil, err
}
posts, ok := postservice.FromContext(ctx)
if !ok {
return nil, fmt.Errorf("post service is missing from application context")
}
return &PostsController{BaseController: base, posts: posts}, nil
}
func (c *PostsController) Show(postID int) error {
post, err := c.posts.Find(postID)
if err != nil {
return err
}
c.Set("post", post)
return nil
}
Controllers/Base
// app/controllers/base_controller.go
package controllers
import (
"context"
"errors"
"net/http"
"golazy.dev/lazycontroller"
)
var ErrLoginRequired = errors.New("login required")
type BaseController struct {
lazycontroller.Base
}
func NewBaseController(ctx context.Context) (BaseController, error) {
base, err := lazycontroller.NewBase(ctx)
if err != nil {
return BaseController{}, err
}
return BaseController{Base: base}, nil
}
func (c *BaseController) HandleError(w http.ResponseWriter, r *http.Request, err error) error {
if errors.Is(err, ErrLoginRequired) {
return c.RedirectToRoute("login", lazycontroller.RedirectStatus(http.StatusSeeOther))
}
return c.Base.HandleError(w, r, err)
}
Controllers/Generators
// app/controllers/post_controller/postcontroller.go
package postcontroller
import (
"fmt"
"net/http"
"golazy.dev/lazycontroller"
)
type Post struct {
ID int
Title string
}
func (c *PostsController) GenPost(postID int) (*Post, error) {
post, err := c.posts.Find(postID)
if err != nil {
return nil, lazycontroller.Error(http.StatusNotFound, fmt.Errorf("post not found"))
}
return post, nil
}
func (c *PostsController) Show(post *Post) error {
c.Set("post", post)
return nil
}
Controllers/BeforeFilters
// app/controllers/base_controller.go
package controllers
type User struct {
ID string
}
type AuthenticatedUser struct {
*User
}
func (c *BaseController) GenUser() (*User, error) {
value, ok, err := c.SessionGet("user_id")
if err != nil {
return nil, err
}
userID, _ := value.(string)
if !ok || userID == "" {
return nil, nil
}
return &User{ID: userID}, nil
}
func (c *BaseController) GenAuthenticatedUser(user *User) (*AuthenticatedUser, error) {
if user == nil {
return nil, ErrLoginRequired
}
return &AuthenticatedUser{User: user}, nil
}
func (c *BaseController) BeforeAction(user *User) error {
c.Set("current_user", user)
return nil
}
// app/controllers/admin_controller/admincontroller.go
package admincontroller
import "sample_app/app/controllers"
func (c *AdminController) Index(user *controllers.AuthenticatedUser) error {
c.Set("title", "Admin")
c.Set("user", user)
return nil
}
Controllers/ContentTypes
// app/controllers/post_controller/postcontroller.go
package postcontroller
import (
"net/http"
"golazy.dev/lazycontroller"
)
var Markdown = lazycontroller.NewFormat(
"text/markdown",
lazycontroller.As("markdown"),
lazycontroller.Suffix("md", "markdown"),
)
func (c *PostsController) Show(w http.ResponseWriter, post *Post) error {
return c.Wants(lazycontroller.Formats{
lazycontroller.HTML: func() error {
c.Set("post", post)
return nil
},
Markdown: func() error {
c.ContentType("text/markdown; charset=utf-8")
_, err := w.Write([]byte("# " + post.Title + "\n"))
return err
},
})
}
Controllers/LazyLoading
// app/controllers/post_controller/postcontroller.go
package postcontroller
import "context"
func (c *PostsController) Show(post *Post) error {
c.Set("post", post)
// SetLater starts loading immediately; use it where older notes say SetEventually.
c.SetLater("related_posts", func(ctx context.Context) ([]Post, error) {
return c.posts.Related(ctx, post.ID)
})
// SetWhenNeeded waits until the template first reads the value.
c.SetWhenNeeded("comment_count", func(ctx context.Context) (int, error) {
return c.posts.CommentCount(ctx, post.ID)
})
return nil
}
Controllers/Session
// init/app.go
package appinit
import (
"os"
"golazy.dev/lazyapp"
"golazy.dev/lazysession"
"sample_app/app"
)
func App() *lazyapp.App {
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Dependencies: Dependencies,
Sessions: lazysession.Config{
Key: os.Getenv("SESSION_KEY"),
},
})
}
// app/controllers/session_controller/sessioncontroller.go
package sessioncontroller
import (
"net/http"
"golazy.dev/lazycontroller"
)
func (c *SessionsController) Create(form *PasswordForm) error {
if err := c.SessionSet("user_id", form.Username); err != nil {
return err
}
if err := c.FlashSet("notice", "Signed in"); err != nil {
return err
}
return c.RedirectToRoute("home", lazycontroller.RedirectStatus(http.StatusSeeOther))
}
func (c *SessionsController) Delete() error {
if err := c.SessionDelete("user_id"); err != nil {
return err
}
return c.RedirectToRoute("home", lazycontroller.RedirectStatus(http.StatusSeeOther))
}
Controllers/Variants
// app/controllers/post_controller/postcontroller.go
package postcontroller
func (c *PostsController) Show(post *Post) error {
if post.Featured {
c.Variants("featured")
}
c.Set("post", post)
return nil
}
# app/views/posts/show.html.tpl
# app/views/posts/show.html+featured.tpl
# app/views/posts/_card.html.tpl
# app/views/posts/_card.html+featured.tpl
Forms
// app/controllers/session_controller/password_form.go
package sessioncontroller
type PasswordForm struct {
Username string `validate:"presence"`
Password string `validate:"presence"`
}
func (c *SessionsController) GenPasswordForm() (*PasswordForm, error) {
form := &PasswordForm{}
if err := c.Decode(form); err != nil {
return nil, err
}
return form, nil
}
// app/controllers/session_controller/sessioncontroller.go
package sessioncontroller
import (
"net/http"
"golazy.dev/lazycontroller"
"golazy.dev/lazyerrors"
)
func (c *SessionsController) New() error {
c.Set("form", &PasswordForm{})
return nil
}
func (c *SessionsController) Create(form *PasswordForm) error {
if err := lazyerrors.Validator(form); err != nil {
c.Status(http.StatusUnprocessableEntity)
c.Set("form", form)
c.Set("session_errors", err)
return c.Render("new")
}
return c.RedirectToRoute("home", lazycontroller.RedirectStatus(http.StatusSeeOther))
}
{{/* app/views/session/new.html.tpl */}}
{{ form_for .form . (form_action "/session") (form_file "session_fields") }}
{{/* app/views/session/_session_fields.html.tpl */}}
{{ text_field "Username" }}
{{ password_field "Password" }}
{{ range field_errors_for .session_errors "username" }}
<p>{{.}}</p>
{{ end }}
{{ submit_button "Sign in" }}
SEO
// init/seo.go
package appinit
import (
"context"
"golazy.dev/lazyseo"
)
func SEO(ctx context.Context) []lazyseo.Option {
return []lazyseo.Option{
lazyseo.SiteName("Sample App"),
lazyseo.Description("A small GoLazy application."),
lazyseo.Language("en"),
lazyseo.Locale("en_US"),
lazyseo.Type("website"),
}
}
// init/app.go
package appinit
func App() *lazyapp.App {
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Dependencies: Dependencies,
SEO: SEO,
})
}
{{/* app/views/layouts/app.html.tpl */}}
<!doctype html>
<html lang="{{seo_lang}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{seo}}
{{stylesheet "/styles.css"}}
</head>
<body>{{.content}}</body>
</html>
// app/controllers/post_controller/postcontroller.go
package postcontroller
func (c *PostsController) Show(post *Post) error {
c.Title(post.Title)
c.Description(post.Summary)
c.Canonical("https://example.com/posts/" + post.Slug)
c.Set("post", post)
return nil
}
SEO/href-lang-tags
// app/controllers/post_controller/postcontroller.go
package postcontroller
func (c *PostsController) Show(post *Post) error {
c.Language(post.Language)
c.Canonical("https://example.com/" + post.Language + "/posts/" + post.Slug)
c.Alternate("en", "https://example.com/en/posts/"+post.Slug)
c.Alternate("de", "https://example.com/de/posts/"+post.Slug)
c.Alternate("x-default", "https://example.com/posts/"+post.Slug)
c.Set("post", post)
return nil
}
SEO/alternates
// app/controllers/post_controller/postcontroller.go
package postcontroller
import "golazy.dev/lazyseo"
func (c *PostsController) Show(post *Post) error {
c.SEO(lazyseo.AlternateLink(lazyseo.Alternate{
Type: "application/rss+xml",
Title: "Posts feed",
URL: "https://example.com/posts/feed.xml",
}))
c.SEO(lazyseo.AlternateLink(lazyseo.Alternate{
Media: "only screen and (max-width: 640px)",
URL: "https://m.example.com/posts/" + post.Slug,
}))
c.Set("post", post)
return nil
}
SEO/Metadata (JSONLD)
// services/postservice/post.go
package postservice
import (
"time"
"golazy.dev/lazyseo"
"golazy.dev/lazyseo/jsonld"
)
type Post struct {
Slug string
TitleValue string
Summary string
ImageURL string
PublishedAt time.Time
UpdatedAt time.Time
}
func (p Post) Title() string { return p.TitleValue }
func (p Post) Description() string { return p.Summary }
func (p Post) Canonical() string { return "https://example.com/posts/" + p.Slug }
func (p Post) Image() string { return p.ImageURL }
func (p Post) Kind() lazyseo.PageKind { return lazyseo.Article }
func (p Post) PublishedTime() time.Time { return p.PublishedAt }
func (p Post) LastUpdated() time.Time { return p.UpdatedAt }
func (p Post) JSONLD() any {
article := jsonld.NewArticle(p.TitleValue)
article.Description = p.Summary
article.URL = p.Canonical()
article.Image = p.ImageURL
article.DatePublished = jsonld.Date(p.PublishedAt)
article.DateModified = jsonld.Date(p.UpdatedAt)
return article
}
// app/controllers/post_controller/postcontroller.go
package postcontroller
import "sample_app/services/postservice"
func (c *PostsController) Show(post postservice.Post) error {
c.Metadata(post)
c.Set("post", post)
return nil
}
Cache/Actions
// app/controllers/post_controller/postcontroller.go
package postcontroller
func (c *PostsController) Show(post *Post) error {
if c.CacheKey(post.ID, post.UpdatedAt) {
return nil
}
c.Set("post", post)
return nil
}
func (c *PostsController) Sidebar() error {
if c.CacheKeyF("posts-sidebar", c.posts.UpdatedAt()) {
return nil
}
c.NoLayout()
c.Set("posts", c.posts.Latest())
return c.Render("sidebar")
}
Cache/Views
{{/* app/views/posts/index.html.tpl */}}
{{ range .posts }}
{{ cache (cache_key "post-card" .ID .UpdatedAt) "post_card" . }}
{{ end }}
{{/* app/views/posts/show.html.tpl */}}
{{ turbo_frame "post" .post (cache_key "post" .post.ID .post.UpdatedAt) (turbo_src .post.URL) }}
{{/* app/views/posts/_post_card.html.tpl */}}
<article>
<h2>{{.Title}}</h2>
<p>{{.Summary}}</p>
</article>
Turbo/Visits
// app/js/app.js
// golazy:turbo
// golazy:stimulus
{{/* app/views/layouts/app.html.tpl */}}
{{importmap "/assets/importmap.json"}}
<script type="module">import "app.js"</script>
{{/* app/views/home/index.html.tpl */}}
<a href="{{path_for "posts"}}" data-turbo-action="advance">Posts</a>
<a href="{{path_for "home"}}" data-turbo="false">Full reload</a>
Turbo/Forms
{{/* app/views/posts/new.html.tpl */}}
{{ turbo_frame "post_form" . }}
{{/* app/views/posts/_post_form_frame.html.tpl */}}
<form action="{{path_for "posts"}}" method="post" data-turbo-frame="post_form">
<label for="post_title">Title</label>
<input id="post_title" name="title" value="{{.form.Title}}">
{{ range field_errors_for .post_errors "title" }}
<p>{{.}}</p>
{{ end }}
<button type="submit">Create post</button>
</form>
Turbo/Frames
{{/* app/views/posts/show.html.tpl */}}
<section>
<h1>{{.post.Title}}</h1>
{{ turbo_frame "comments" . (turbo_src (path_for "post_comments" .post.ID)) (turbo_loading "lazy") }}
</section>
{{/* app/views/posts/_comments_frame.html.tpl */}}
<ol>
{{ range .comments }}
<li>{{.Body}}</li>
{{ else }}
<li>No comments yet.</li>
{{ end }}
</ol>
Turbo/Frames/Controllers
// app/controllers/post_controller/postcontroller.go
package postcontroller
import "golazy.dev/lazyturbo"
func (c *PostsController) Comments(post *Post) error {
c.Set("comments", c.posts.Comments(post.ID))
return c.RenderTurboFrame("comments", lazyturbo.Loading("lazy"))
}
{{/* app/views/posts/_comments_frame.html.tpl */}}
<ol>
{{ range .comments }}
<li>{{.Body}}</li>
{{ else }}
<li>No comments yet.</li>
{{ end }}
</ol>
Turbo/Streams
{{/* app/views/posts/create.turbo_stream.tpl */}}
<turbo-stream action="prepend" target="posts">
<template>
{{ partial "post_card" .post }}
</template>
</turbo-stream>
<turbo-stream action="replace" target="post_form">
<template>
{{ turbo_frame "post_form" . }}
</template>
</turbo-stream>
Turbo/Streams/Controller
// app/controllers/post_controller/postcontroller.go
package postcontroller
import "golazy.dev/lazycontroller"
func (c *PostsController) Create(form *PostForm) error {
post, err := c.posts.Create(form.Title)
if err != nil {
return err
}
c.Set("post", post)
c.Set("form", &PostForm{})
return c.Wants(lazycontroller.Formats{
lazycontroller.HTML: func() error {
return c.RedirectToRoute("post", post.ID)
},
lazycontroller.TurboStream: func() error {
return c.Render("create")
},
})
}
Turbo/ControllerFrames
// app/controllers/post_controller/postcontroller.go
package postcontroller
import "golazy.dev/lazycontroller"
func (c *PostsController) Show(post *Post) error {
c.Set("post", post)
return c.Wants(lazycontroller.Formats{
lazycontroller.HTML: func() error {
return nil
},
lazycontroller.TurboFrame: func() error {
return c.RenderTurboFrame("post")
},
})
}
Turbo/Controller
// app/controllers/post_controller/postcontroller.go
package postcontroller
import (
"net/http"
"golazy.dev/lazycontroller"
)
func (c *PostsController) Update(post *Post, form *PostForm) error {
updated, err := c.posts.Update(post.ID, form.Title)
if err != nil {
return err
}
c.Set("post", updated)
return c.Wants(lazycontroller.Formats{
lazycontroller.HTML: func() error {
return c.RedirectToRoute("post", updated.ID, lazycontroller.RedirectStatus(http.StatusSeeOther))
},
lazycontroller.TurboFrame: func() error {
return c.RenderTurboFrame("post")
},
lazycontroller.TurboStream: func() error {
return c.Render("update")
},
})
}
Tailwind
/* app/styles/application.css */
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
{{/* app/views/layouts/app.html.tpl */}}
{{stylesheet "/styles.css"}}
# Regenerate app/public/styles.css after changing Tailwind input.
lazy tailwind
lazyshaft
# js.toml
[entrypoint.turbo]
module = "@hotwired/turbo"
[entrypoint.stimulus]
module = "@hotwired/stimulus"
# Regenerate app/public/assets/importmap.json and app/public/assets/lazyshaft.
lazy js
{{/* app/views/layouts/app.html.tpl */}}
{{importmap "/assets/importmap.json"}}
<script type="module">import "app.js"</script>
Js
// app/js/app.js
// golazy:turbo
// golazy:stimulus
// app/js/controllers/counter_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["output"]
static values = { count: { type: Number, default: 0 } }
increment() {
this.countValue += 1
this.outputTarget.textContent = String(this.countValue)
}
}
{{/* app/views/home/index.html.tpl */}}
<div data-controller="counter">
<output data-counter-target="output">0</output>
<button type="button" data-action="counter#increment">Increment</button>
</div>
Assets
// app/files.go
package app
import (
"embed"
"golazy.dev/lazyapp"
)
//go:embed views public
var Files embed.FS
var Views = lazyapp.MustSub(Files, "views")
var Public = lazyapp.MustSub(Files, "public")
// init/app.go
package appinit
import (
"golazy.dev/lazyapp"
"sample_app/app"
)
func App() *lazyapp.App {
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Dependencies: Dependencies,
})
}
{{/* app/views/layouts/app.html.tpl */}}
{{stylesheet "/styles.css"}}
<img src="{{asset_path "/images/logo.svg"}}" alt="Logo">
Mailers
// init/dependencies.go
package appinit
import (
"context"
"fmt"
"net/smtp"
"os"
"golazy.dev/lazydeps"
"golazy.dev/lazymailer"
)
func Dependencies(deps *lazydeps.Scope) error {
_, err := lazydeps.Service(deps, "mailer", func(ctx context.Context) (
context.Context,
*lazymailer.Mailer,
error,
context.CancelFunc,
) {
deliveries := lazymailer.NewRegistry("default", map[string]lazymailer.Delivery{
"default": lazymailer.SMTPDelivery{
Addr: os.Getenv("SMTP_ADDR"),
Auth: smtp.PlainAuth("", os.Getenv("SMTP_USER"), os.Getenv("SMTP_PASSWORD"), os.Getenv("SMTP_HOST")),
},
})
mailer, err := lazymailer.New(ctx, deliveries)
if err != nil {
return ctx, nil, fmt.Errorf("initialize mailer: %w", err), nil
}
return lazymailer.WithContext(ctx, mailer), mailer, nil, nil
})
return err
}
// app/mailers/notice_mailer/noticemailer.go
package noticemailer
import (
"context"
"golazy.dev/lazymailer"
)
type NoticeMailer struct {
base lazymailer.Base
}
func New(ctx context.Context) (*NoticeMailer, error) {
base, err := lazymailer.NewBase(ctx, "notice_mailer", lazymailer.Defaults{
From: lazymailer.MustParseAddress("GoLazy <[email protected]>"),
Delivery: "default",
Layout: "mailer",
})
if err != nil {
return nil, err
}
return &NoticeMailer{base: base}, nil
}
func (m *NoticeMailer) Welcome(to lazymailer.Address, name string) error {
m.base.Set("name", name)
return m.base.Mail(lazymailer.Options{
Action: "welcome",
To: []lazymailer.Address{to},
Subject: "Welcome",
})
}
{{/* app/views/notice_mailer/welcome.html.tpl */}}
<p>Hello {{.name}}</p>
Jobs
// app/jobs/basejob/basejob.go
package basejob
import "golazy.dev/lazyjobs"
type BaseJob struct {
lazyjobs.BaseJob
}
// app/jobs/send_welcome/sendwelcome.go
package sendwelcome
import (
"context"
"sample_app/app/jobs/basejob"
)
type Job struct {
basejob.BaseJob
UserID string `json:"user_id"`
}
func (*Job) Kind() string { return "mail.send_welcome" }
func (j *Job) Work(ctx context.Context) error {
// Load services from ctx and send the welcome message for j.UserID.
return nil
}
// app/jobs/jobs.go
package jobs
import (
"golazy.dev/lazyjobs"
"sample_app/app/jobs/send_welcome"
)
func DefinedJobs(runner *lazyjobs.JobRunner) {
runner.MustRegister(&sendwelcome.Job{})
}
// init/app.go
package appinit
import (
"golazy.dev/lazyapp"
"golazy.dev/lazyjobs"
"sample_app/app"
"sample_app/app/jobs"
)
func App() *lazyapp.App {
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Dependencies: Dependencies,
Jobs: lazyapp.Jobs(lazyjobs.Config{
Define: jobs.DefinedJobs,
Workers: 2,
}),
})
}
Services
// services/postservice/postservice.go
package postservice
import "context"
type Post struct {
ID int
Title string
}
type Service interface {
Latest() []Post
Find(id int) (*Post, error)
}
type service struct{}
type contextKey struct{}
func New() Service {
return service{}
}
func WithContext(ctx context.Context, service Service) context.Context {
return context.WithValue(ctx, contextKey{}, service)
}
func FromContext(ctx context.Context) (Service, bool) {
service, ok := ctx.Value(contextKey{}).(Service)
return service, ok
}
func (service) Latest() []Post {
return []Post{{ID: 1, Title: "Hello"}}
}
func (service) Find(id int) (*Post, error) {
return &Post{ID: id, Title: "Hello"}, nil
}
// init/dependencies.go
package appinit
import (
"context"
"golazy.dev/lazydeps"
"sample_app/services/postservice"
)
func Dependencies(deps *lazydeps.Scope) error {
_, err := lazydeps.Service(deps, "postservice", func(ctx context.Context) (
context.Context,
postservice.Service,
error,
context.CancelFunc,
) {
service := postservice.New()
return postservice.WithContext(ctx, service), service, nil, nil
})
return err
}
PWA
// init/app.go
package appinit
import (
"golazy.dev/lazyapp"
"golazy.dev/lazypwa"
"sample_app/app"
)
func App() *lazyapp.App {
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
PWA: lazypwa.Config{
Installable: true,
Manifest: lazypwa.ManifestConfig{
Name: "Sample App",
ShortName: "Sample",
StartURL: "/",
Scope: "/",
Display: "standalone",
ThemeColor: "#FDDD00",
},
Offline: lazypwa.OfflineConfig{
Enabled: true,
FallbackURL: "/",
URLs: []string{"/"},
IncludeAssets: true,
},
},
})
}
{{/* app/views/layouts/app.html.tpl */}}
<head>
{{seo}}
{{pwa}}
</head>
MCP
// app/mcps/admin_mcp/adminmcp.go
package adminmcp
import (
"context"
"golazy.dev/lazymcp"
)
type AdminMCP struct {
lazymcp.Base
}
func New(ctx context.Context) *AdminMCP {
return &AdminMCP{Base: lazymcp.NewBase(ctx)}
}
type UserCountParams struct {
Active bool `json:"active"`
}
type UserCountResult struct {
Count int `json:"count"`
}
func (m *AdminMCP) UserCountTool(ctx context.Context) lazymcp.ToolSpec {
return lazymcp.ToolSpec{
Desc: "Count users.",
Fn: m.UserCount,
UI: lazymcp.UI("ui://admin/dashboard"),
}
}
func (m *AdminMCP) UserCount(ctx context.Context, params UserCountParams) (UserCountResult, error) {
return UserCountResult{Count: 3}, nil
}
func (m *AdminMCP) DashboardApp(ctx context.Context) lazymcp.AppSpec {
return lazymcp.AppSpec{
Name: "dashboard",
Desc: "Admin dashboard.",
View: "dashboard",
UseLayout: true,
}
}
// init/mcp.go
package appinit
import (
"context"
"golazy.dev/lazymcp"
adminmcp "sample_app/app/mcps/admin_mcp"
)
func RegisterMCP(ctx context.Context, scope *lazymcp.Scope) error {
return scope.Register(adminmcp.New(ctx))
}
// init/app.go
package appinit
import (
"golazy.dev/lazyapp"
"sample_app/app"
)
func App() *lazyapp.App {
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Dependencies: Dependencies,
MCP: RegisterMCP,
})
}
{{/* app/views/mcp/admin/dashboard.html.tpl */}}
<section>
<h1>Admin</h1>
<p>Rendered as an MCP Apps UI resource.</p>
</section>
See Others
- Basic Routes
- Controller Architecture
- Actions
- Generator Arguments
- Formats And MIME
- Forms
- SEO And Sitemaps
- Caching
- Hotwire Turbo
- Turbo Frames
- Server-Rendered Updates
- Stylesheets And Tailwind
- lazy js And js.toml
- Stimulus Controllers
- Embedded Assets
- Mailers
- Background Jobs
- Context And Services
- PWA, Workers And Notifications
- MCP Server
- Packages