Guide
Requirements and installation
Go 1.27 or newer. Install the CLI:
go install github.com/dayanchm/golar/cmd/golar@v0.5.1
Make sure $(go env GOPATH)/bin is on your PATH. From a checkout of this
repository, go install ./cmd/golar installs the development version.
Applications that only use the framework can add it with
go get github.com/dayanchm/golar@v0.5.1. Changes are listed in
CHANGELOG.md.
Quick start
Create a project:
golar new my-api --no-interactive --template api --database sqlite --auth token
cd my-api
go mod tidy
golar migrate
golar serve --addr :8080
Or use the framework directly:
package main
import (
"log"
"net/http"
"github.com/dayanchm/golar"
"github.com/dayanchm/golar/http/middleware"
)
type CreateUser struct {
Name string `json:"name" validate:"required,max=100"`
Email string `json:"email" validate:"required,email"`
}
func main() {
app := golar.New()
app.Use(middleware.RequestID(), middleware.Logger(), middleware.Recovery(), middleware.SecureHeaders())
app.Get("/users/{id}", func(c *golar.Context) error {
return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
})
app.Post("/users", func(c *golar.Context) error {
var in CreateUser
if err := c.Validate(&in); err != nil {
return err // 400 for bad JSON, 422 with field errors
}
return c.JSON(http.StatusCreated, in)
})
if err := app.Run(":8080"); err != nil {
log.Fatal(err)
}
}
go run ./examples/basic-api runs a similar example with /, /health,
GET /api/users/{id} and POST /api/users.
CLI
golar new my-app --interactive # prompts for every option
golar routes # lists the application's registered routes
golar doctor # checks files, dependencies, tools and configuration
golar serve --addr :8080 # builds and runs the app; also runs Vite for frontends
golar build # npm run build, then go build into bin/server
golar make:migration create_posts_table
golar make:request StorePost # app/requests/store_post_request.go
golar make:frontend react # adds a Vite frontend to a project without one
golar migrate # also migrate:status, :rollback, :reset, :refresh, --pretend
golar new option |
Choices | Default |
|---|---|---|
--template |
api, web, fullstack |
api |
--database |
none, sqlite, postgres, mysql |
none |
--frontend |
none, react, vue, svelte |
react for Full Stack; otherwise none |
--auth |
none, token, session (both need a database) |
none |
--docker |
boolean | false |
--module |
Go module path | project name |
--dir |
parent directory for new; project directory for other commands |
current directory |
golar new prompts on a terminal; --no-interactive (or --yes) uses defaults
and flags for scripts. It validates every file before writing and refuses
existing destinations, including empty directories and symlinks. It does not
download dependencies or initialize Git. For projects with a frontend,
golar serve runs the Vite dev server alongside Go for hot reloading; Go code
changes still need a restart.
Generated projects
Projects contain go.mod, .env (mode 0600), .env.example, golar.yaml,
handlers, models, routes, bootstrap and config code, migration and storage
folders, and runnable tests. The default middleware stack adds request IDs,
logging, panic recovery, security headers, a 10 MiB body limit and gzip.
- Frontends use Vite. Full Stack projects are
single-page apps that Go serves from
frontend/dist; Web projects render pages in Go and load the bundle with{{vite "src/main.js"}}.golar serveruns Vite with Go, andgolar buildbuilds both. Node 24 is recommended. See the frontend guide. - Databases: SQLite creates its file; PostgreSQL and MySQL need a running
server. Configure
DATABASE_DSNin.envand rungolar migrate. - Authentication:
--auth tokenand--auth sessiongenerate aUsermodel, a users migration and rate-limited/api/auth/register,login,logoutandmeroutes. Token projects issue personal access tokens forAuthorization: Bearer; session projects use a cookie with CSRF protection. Projects generated before v0.4.0 keep their sharedAUTH_TOKEN. - Docker: run
go mod tidy && go mod vendor, thendocker compose up --build. Compose adds a PostgreSQL or MySQL service, or persistent storage for SQLite. Its credentials are development defaults.
golar.yaml records the scaffolding choices and default address. .env and
process variables configure runtime values; process values win, and
serve --addr wins over both. Editing the manifest does not regenerate a
project. It supports flat scalars and JSON, not nested YAML.
Framework source
Inside a Golar checkout the CLI detects the source tree and adds a replace
directive; pass --framework-dir /path/to/golar to choose it explicitly.
Elsewhere, projects require the tagged release given by --framework-version
(default v0.5.1). Remove local replacements before distributing a project.
Applications and errors
golar.New(golar.Config{...}) creates an application. app.Run() listens on the
configured address (default :8080), app.Run(":9000") overrides it and
app.RunContext(ctx) stops when ctx is canceled. Configure routes, middleware,
hooks and error handling before serving; an application runs once.
app.OnStart(func(context.Context) error) hooks run in order after the address
is bound and before serving. app.OnShutdown hooks run in reverse order after
requests drain, also when a startup hook fails, with a fresh context bounded by
the shutdown timeout. Server and hook errors are returned from Run and work
with errors.Is.
Handlers receive *golar.Context and return an error:
| Returned error | Response |
|---|---|
&golar.HTTPError{Code: 404, Message: "Post not found"} |
that status and message |
*validation.Error, for example from c.Validate |
422 {"errors": {"field": ["message"]}} |
*http.MaxBytesError, from the body limits or BodyLimit |
413 |
httpcontext.ErrUnsupportedMediaType from c.Bind |
415 |
a body c.Bind cannot decode, or a missing upload |
400 Invalid request body |
*httpcontext.InputError, for example from c.QueryInt |
400 Invalid query parameter "page" |
| anything else | logged, generic 500 |
Once a response has started, later errors are only logged. Replace the handler
with app.SetErrorHandler(func(c *golar.Context, err error) { ... }); nil
restores the default. Mount standard handlers with golar.Adapt(handler).
Validation
type RegisterRequest struct {
Name string `json:"name" validate:"required,max=100"`
Email string `json:"email" validate:"required,email,unique=users.email"`
Password string `json:"password" validate:"required,min=8,confirmed"`
}
Rules include required, email, url, uuid, numeric, string,
integer, boolean, alpha, alpha_num, alpha_dash, starts_with,
ends_with, date, min, max, len, between, in, regex,
confirmed, unique and exists. Slices, arrays and maps are validated
element by element (items.0.sku, each rules), messages can be localized
(Turkish is built in), and request structs can add Authorize, Prepare and
AfterValidate hooks; golar make:request generates one. See the
validation guide.
API resources
func UserResource(ctx context.Context, u models.User) resource.Object {
return resource.Object{"id": u.ID, "name": u.Name, "email": resource.When(isAdmin(ctx), u.Email)}
}
users, page, err := orm.Query[models.User](db).Paginate(ctx, pageNumber, 15)
return c.JSON(http.StatusOK, resource.Paginated(ctx, users, page, UserResource, resource.PageURL(c.Request, "page")))
Resources name every field they send, so private fields stay private.
Item, Collection and Paginated wrap them in {"data": ...} with
pagination meta and links. See the resources guide.
Request context
app.Post("/avatar", func(c *golar.Context) error {
page, err := c.QueryInt("page", 1)
if err != nil {
return err // 400 Invalid query parameter "page"
}
file, err := c.File("avatar")
if err != nil {
return err // 400 when missing, 413 when too large
}
if err := c.SaveFile(file, filepath.Join("storage", "avatars", rand.Text())); err != nil {
return err
}
if err := c.SetCookie("last_page", strconv.Itoa(page), 24*time.Hour); err != nil {
return err
}
return c.Redirect(http.StatusSeeOther, "/profile")
})
c.Bind and c.Validate read JSON, URL-encoded or multipart bodies. Bodies are
limited to 4 MiB, or 32 MiB for multipart, unless changed with
app.SetContextConfig. Responses include JSON, String, HTML, Render,
Blob, NoContent, Redirect and Download; c.IP() honours
X-Forwarded-For only from trusted proxies. See the
request context guide.
Router
app.Get("/users/{id}", showUser)
app.Get("/users/new", newUser) // static segments win over parameters
api := app.Group("/api").Group("/v1")
api.Post("/users", createUser)
api.Put("/users/{id}", replaceUser)
api.Patch("/users/{id}", updateUser)
api.Delete("/users/{id}", deleteUser)
Read parameters with c.Param("id"), or r.PathValue("id") when using
github.com/dayanchm/golar/http/router directly with standard handlers.
- Static segments take priority over parameters from left to right, regardless of registration order.
- Registering the same method and path twice panics during setup.
- Unknown paths return 404. Known paths with another method return 405 with a
sorted
Allowheader. - HEAD is served by the GET route unless a HEAD route exists. OPTIONS is not
registered implicitly;
middleware.CORSanswers preflight requests.
Middleware
app.Use(
middleware.RequestID(),
middleware.Logger(),
middleware.Recovery(),
middleware.SecureHeaders(),
middleware.CORS(middleware.CORSConfig{AllowOrigins: []string{"https://app.example.com"}}),
middleware.BodyLimit(10 << 20),
middleware.Timeout(30 * time.Second),
middleware.Compress(),
)
Middleware runs in registration order, with the first outermost, and wraps 404
and 405 responses too. Every middleware is opt-in and configurable; see the
middleware guide. Custom middleware has the standard
func(http.Handler) http.Handler shape. Use http.NewResponseController(w) to
flush or hijack through the wrappers.
HTTP server
Run handles SIGINT and SIGTERM, stops accepting connections and waits up to 10
seconds for active requests. For custom timeouts, use http/server:
srv := server.New(app, server.Config{
Addr: ":8080",
ReadTimeout: 15 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
ShutdownTimeout: 10 * time.Second,
})
err := srv.Run(ctx)
These are the defaults. A shutdown that times out force-closes connections and returns an error. Hijacked connections such as WebSockets need their own shutdown handling.
Database
config, err := database.ConfigFromEnv() // DB_DRIVER, DATABASE_DSN or DB_HOST, DB_DATABASE, ...
db, err := database.Connect(config)
rows, err := db.Table("users").
Select("id", "name").
Where("active", "=", true).
OrderBy("id", "desc").
Limit(20).
QueryContext(ctx)
- Connections: validated
DB_*configuration, pooling, named connections, context-aware raw SQL and panic-safe transactions. - Migrations and schema: Go migrations with
Up/Down, batches, locking and--pretend; tables, columns, indexes, foreign keys, native alters and SQLite rebuilds. - Query builder: immutable, parameterized SELECT, INSERT, UPDATE and DELETE
with joins,
WhereIn, aggregates and pagination.UpdateandDeleterequire aWhereclause or an explicitAllRows(). - ORM: plain struct models, CRUD,
hasOne,hasMany,belongsToandbelongsToMany, N+1-free eager loading and soft deletes.
type User struct {
orm.Model
Name string
Posts []Post `orm:"hasMany"`
}
err := orm.Create(ctx, db, &user)
users, err := orm.Query[User](db).Where("active", "=", true).With("Posts").Get(ctx)
Guides: configuration,
connections,
migrations,
schema builder,
transactions,
query builder and ORM.
go run ./examples/database runs a complete SQLite example.
Authentication
sessions := &session.Manager{Store: &session.SQLStore{DB: db}} // or session.NewManager() in memory
guard := &auth.SessionGuard{Provider: &auth.ORMProvider[User]{DB: db}}
app.Use(sessions.Middleware, auth.CSRF)
user, err := (&auth.Authenticator{Provider: provider}).Attempt(ctx, email, password)
err = guard.Login(r, user)
Sessions can live in memory or in a SQL table that survives restarts and is shared between instances, with session IDs stored as SHA-256 hashes. Argon2id password hashing, session and bearer-token guards, route protection, gates, policies, roles and permissions, password resets, email verification, rate limiting and CSRF protection are described in the authentication guide.
mailer, err := mail.FromEnv() // MAIL_DRIVER=smtp|log, MAIL_HOST, MAIL_FROM_ADDRESS, ...
err = mailer.Send(ctx, mail.Message{To: []string{"ada@example.com"}, Subject: "Hi", Text: "Hello"})
broker.Send = auth.PasswordResetMail{Mailer: mailer, URL: "https://app.example.com/reset-password"}.Send
fake := mail.NewFake() // in tests
fake.AssertSent(t, "ada@example.com")
Generated projects with authentication send password reset and verification
emails from .env settings alone; they log mail until MAIL_DRIVER=smtp is
set. See the mail guide.
Development
go vet ./...
go test -race ./...
go build ./...
CI checks formatting, vet, tests with race detection, coverage and builds (ci.yml); runs the database, auth, validation and session store tests against real SQLite, PostgreSQL and MySQL (database.yml); and generates, migrates and tests projects for every template, frontend and auth mode (scaffold.yml).
Roadmap
Planned work is tracked in GitHub milestones.
With the mailer, the planned v0.5.0 features are in place. AI-native tooling,
such as a golar mcp command that exposes routes, schema and models to coding
agents, is planned but not yet available.