Lilium is a lightweight and high-performance web framework for Go β built around clarity, modularity, and developer experience.
It brings together:
- β‘ Chi-powered high-performance router
- π§© Modular App Container + Dependency Injection
- π Smart YAML Configuration with ENV expansion
- π± Automatic Defaults for common fields
- π§© Extensible config via Extras (for plugins/modules)
- π§΅ Asynchronous Zerolog logging
- π Built-in EventBus (pub/sub)
- ποΈ Static File Serving
- π‘οΈ Composable Middleware
- π§Ή Graceful Shutdown & Lifecycle Hooks
- π§ͺ First-class Testability
Designed to stay idiomatic to Go while providing a clean, modern developer experience.
A frictionless wrapper around Chi:
- Grouped & nested routes
- Typed
RequestContext - Middleware chaining
- Helpers for JSON / Text / HTML responses
- Centralized error handling
- Optional automatic route logging
Example:
router.GET("/hello/{name}", func(c *core.RequestContext) error {
return c.JSON(200, map[string]string{
"message": "Hello " + c.Param("name"),
})
})Declare static directories directly in config:
server:
static:
- route: "/"
directory: "./public"Automatically mounted at startup.
Load config with one line:
cfg := config.Load("lilium.yaml")| Feature | Status |
|---|---|
| Environment variable expansion | β |
| Fallback default values | β |
| Unknown fields preserved for modules | β |
| Strongly typed configuration | β |
Supports ${VAR} and ${VAR:default}:
server:
port: ${PORT:8080}| Scenario | Result |
|---|---|
PORT exists |
use PORT value |
PORT missing |
use 8080 |
If omitted:
| Field | Default |
|---|---|
name |
"Lilium" |
server.port |
8080 |
server.cors.maxAge |
600 seconds |
| Logger output | toStdout = true |
| Logger prefix | "[Lilium] " |
env.enableFile |
false |
If .env enabled filePath empty |
.env |
This means you can start with only:
server:
port: 9000β Completely valid π
Unknown YAML fields are stored in:
cfg.Extras map[string]anyUsed for modules:
auth:
provider: google
tokenTTL: 3600Module usage:
type AuthConfig struct {
Provider string `yaml:"provider"`
TokenTTL int `yaml:"tokenTTL"`
}
var auth AuthConfig
_ = cfg.GetExtra("auth", &auth)This enables plugin systems and forward-compatible configuration.
- Async writes
- File + STDOUT targets
- Debug mode
- Auto-flush on shutdown
logger:
toStdout: true
prefix: "[MyApp] "
debugEnabled: trueSimple in-process pub/sub:
id, ch, _ := app.Context.Bus.Subscribe("notifications", 10)
go func() {
for msg := range ch {
fmt.Println("received:", msg)
}
}()
app.Context.Bus.Publish("notifications", "hello world")Perfect for background processing and modular integrations.
Lightweight DI for shared dependencies:
app.Context.Provide("db", db)
db := app.Context.MustGet("db").(*sql.DB)Per-request context includes logging + utilities.
On termination:
- Stop HTTP server cleanly
- Drain in-flight requests
- Flush logs
- Close EventBus
- Trigger module lifecycle hooks
go get github.com/spyder01/lilium-go@latest.
βββ lilium.yaml
βββ main.go
βββ public/
main.go
package main
import (
"github.com/spyder01/lilium-go/pkg/core"
"github.com/spyder01/lilium-go/pkg/config"
)
func main() {
cfg := config.Load("lilium.yaml")
app := core.New(cfg)
router := core.NewRouter(app.Context)
router.GET("/", func(c *core.RequestContext) error {
return c.Text(200, "Welcome to Lilium!")
})
app.Start(router)
}All router + RequestContext behavior is testable:
req := httptest.NewRequest("GET", "/ping", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
assert.Equal(t, 200, rec.Code)- ENV var expansion in config
- Unknown field
Extrasfor modules - Authentication (sessions + JWT)
- Built-in validators
- WebSockets
- Rate-limiting & caching middleware
- Auto OpenAPI generation
- CLI tooling (
lilium new, scaffolding) - Stronger DI capabilities
PRs welcome! Open an issue for ideas or bugs.
MIT Β© 2025