AdonisJS-style web framework for Go. Convention over configuration, clear structure, and a pleasant DX.
Repository: github.com/CodeSyncr/nimbus
- Router – Express-style routes with
:paramplaceholders, route groups, and middleware - Context – Request/response helpers:
JSON(),Param(),Redirect() - Config – Environment-based config (
.env+config/) - Middleware – Global and per-route middleware (Logger, Recover, CORS)
- Validation – Struct validation with go-playground/validator
- Database – GORM-based models with
database.Model(ID, timestamps), migrations support - CLI –
nimbus new,make:model,make:migration(Ace-style)
├── app/
│ ├── controllers/
│ ├── models/
│ └── middleware/
├── bin/ # Server boot (bin/server.go)
├── config/
├── database/
│ └── migrations/
├── start/ # Routes, kernel (optional)
├── public/
├── main.go
├── go.mod
└── .env
From the nimbus repo directory:
cd /path/to/nimbus
go install ./cmd/nimbusIf you get zsh: command not found: nimbus, add Go’s bin directory to your PATH. For zsh, run once:
echo 'export PATH="$HOME/go/bin:$PATH"' >> ~/.zshrc && source ~/.zshrcThen run nimbus again. You can also run your app without the CLI: go run main.go (no hot reload) or go run github.com/air-verse/air@v1.52.3 (hot reload).
Hot reload: nimbus serve runs air via go run, so you don’t install anything extra. The first run may download air once; after that, edits to .go and .nimbus files restart the app automatically. No need to add air to your app’s go.mod or run go mod tidy for it. Press Ctrl+C to stop the server; it shuts down gracefully and releases the port.
nimbus new myapp
cd myapp
go mod tidy
nimbus serveServer runs at http://localhost:3333. You can also run go run main.go directly.
If you see reading ../go.mod: no such file or directory when running nimbus serve: your app’s go.mod has replace github.com/CodeSyncr/nimbus => ../, which points at the parent directory. If the app lives outside the nimbus repo (e.g. as a sibling), change it to:
replace github.com/CodeSyncr/nimbus => ../nimbusSo the path after => is the directory that contains the nimbus go.mod.
If you see missing go.sum entry for module providing package ... (imported by github.com/CodeSyncr/nimbus/...): your app’s go.sum is missing transitive dependencies from the local nimbus module. From your app directory run:
go mod tidyThen run nimbus serve again.
package main
import (
"github.com/CodeSyncr/nimbus"
"github.com/CodeSyncr/nimbus/context"
"github.com/CodeSyncr/nimbus/middleware"
"github.com/CodeSyncr/nimbus/http"
)
func main() {
app := nimbus.New()
app.Router.Use(middleware.Logger(), middleware.Recover())
app.Router.Get("/", func(c *http.Context) error {
return c.JSON(httpx.StatusOK, map[string]string{"hello": "nimbus"})
})
app.Router.Get("/users/:id", func(c *http.Context) error {
return c.JSON(httpx.StatusOK, map[string]string{"id": c.Param("id")})
})
// Route groups
api := app.Router.Group("/api")
api.Get("/posts", listPosts)
api.Post("/posts", createPost)
_ = app.Run()
}Set PORT, APP_ENV, APP_NAME, DB_DRIVER, DB_DSN in .env. Config is loaded via config.Load() in nimbus.New().
import "github.com/CodeSyncr/nimbus/database"
// Connect (e.g. in bin/server.go)
db, _ := database.Connect(config.Database.Driver, config.Database.DSN)
// Model (embed database.Model)
type User struct {
database.Model
Name string
Email string
}
db.AutoMigrate(&User{})Run migrations from your app root:
nimbus db:migrateOr directly: go run . migrate. Migrations use an AdonisJS Lucid-style schema builder. Create one with:
nimbus make:migration create_usersThen add the new migration to database/migrations/registry.go. Each migration runs once; already-run migrations are tracked in schema_migrations and shown as skipped on subsequent runs.
import "github.com/CodeSyncr/nimbus/validation"
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2"`
Email string `json:"email" validate:"required,email"`
}
func createUser(c *http.Context) error {
var req CreateUserRequest
if err := validation.ValidateRequestJSON(c.Request.Body, &req); err != nil {
return c.JSON(http.StatusUnprocessableEntity, map[string]any{"errors": err})
}
// ...
}Put templates in a views/ folder with the .nimbus extension. Use c.View("name", data) to render (like Edge in AdonisJS).
Syntax (Edge-aligned):
| Nimbus | Description |
|---|---|
{{ variable }} |
Output, HTML-escaped |
{{{ variable }}} |
Output, unescaped (for rich content) |
{{-- comment --}} |
Comment (stripped from output) |
@if(cond) … @elseif(cond) … @else … @endif |
Conditionals |
@each(items) … @endeach |
Loop; use {{ . }} for current item |
@each(post in posts) … @endeach |
Loop with named var; use {{ $post }} |
@layout('layout') |
Wrap with layout; layout uses {{ .embed }} or {{ .content }} |
{{ .csrfField }} |
CSRF hidden input (auto-injected when Shield enabled) |
@dump(posts) |
Debug: pretty-print variable (or @dump(state) for all) |
@card() … @end |
Component: views/components/card.nimbus becomes @card() |
Components: Create views/components/card.nimbus; use @card() … @end in templates. Render the main slot with {{{ .slots.main }}} (Edge-style).
Example: views/home.nimbus
@layout('layout')
<h2>Hello, {{ name }}!</h2>
@if(.items)
@each(items)
<li>{{ . }}</li>
@endeach
@else
<p>No items.</p>
@endif
In your handler:
return c.View("home", map[string]any{"name": "Guest", "title": "Home", "items": []string{"A", "B"}})Views are loaded from the views/ directory by default. Change with view.SetRoot("custom/views") in main.go.
| Plugin | Description | Docs |
|---|---|---|
| Drive | File storage (fs, S3, GCS, R2, Spaces, Supabase) | README |
| Transmit | SSE for real-time server-to-client push | README |
| Package | Description | Docs |
|---|---|---|
| Queue | Background jobs (sync, Redis, database, SQS, Kafka) | README |
| Plugin | Description | Docs |
|---|---|---|
| AI | AI integration (OpenAI, Ollama, Anthropic, etc.) | README |
| Inertia | Inertia.js for Vue/React/Svelte SPAs | README |
| Telescope | Debugging and introspection dashboard | README |
| MCP | Model Context Protocol for AI clients | README |
| Unpoly | Progressive enhancement and partial page updates | nimbus add unpoly |
Redis is used by Queue (QUEUE_DRIVER=redis) and Transmit (TRANSMIT_TRANSPORT=redis) for distributed workers and multi-instance SSE. Set REDIS_URL=redis://localhost:6379 in .env when using these features.
| Command | Description |
|---|---|
nimbus new <name> |
Create a new Nimbus app |
nimbus serve |
Run the app (from app root; like AdonisJS ace serve) |
nimbus db:migrate |
Run database migrations |
nimbus db:rollback |
Rollback the last migration |
nimbus make:model <Name> |
Scaffold a model |
nimbus make:migration <name> |
Scaffold a migration |
nimbus queue:work |
Run the queue worker (processes background jobs) |
nimbus add <plugin> |
Install a plugin (drive, telescope, inertia, ai, mcp, etc.) |
-
Push to GitHub (repo must be public for
go get):git remote add origin https://github.com/CodeSyncr/nimbus.git # if not already set git push -u origin main -
Tag a version (so users can pin versions):
git tag v0.1.0 git push origin v0.1.0
-
Install CLI (others can install from the repo):
go install github.com/CodeSyncr/nimbus/cmd/nimbus@latest
-
Use in another project:
go get github.com/CodeSyncr/nimbus@v0.1.0
After the first fetch, the module appears on pkg.go.dev automatically.
MIT