Controllers

Controllers organize your request handling logic into structs with action methods. Instead of defining inline handler functions for every route, you group related actions into a controller โ€” following Laravel-style organization.

What Is a Controller?

A controller is a Go struct whose methods match the Nimbus handler signature: func(*http.Context) error. Each method represents one action (listing resources, showing a single resource, creating, updating, or deleting).

Creating a Controller

Create a file in app/controllers/. Define a struct and attach action methods:

package controllers

import (
    "github.com/CodeSyncr/nimbus/http"
)

type PostsController struct{}

func (pc *PostsController) Index(c *http.Context) error {
    posts := []map[string]string{
        {"id": "1", "title": "Hello World"},
        {"id": "2", "title": "Getting Started with Nimbus"},
    }
    return c.JSON(http.StatusOK, posts)
}

func (pc *PostsController) Show(c *http.Context) error {
    id := c.Param("id")
    return c.JSON(http.StatusOK, map[string]string{"id": id})
}

func (pc *PostsController) Create(c *http.Context) error {
    return c.JSON(http.StatusCreated, map[string]string{"message": "created"})
}

func (pc *PostsController) Update(c *http.Context) error {
    id := c.Param("id")
    return c.JSON(http.StatusOK, map[string]string{"id": id, "updated": "true"})
}

func (pc *PostsController) Delete(c *http.Context) error {
    id := c.Param("id")
    return c.JSON(http.StatusOK, map[string]string{"id": id, "deleted": "true"})
}

Registering Controller Routes

In main.go, instantiate the controller and bind each action to a route:

posts := &controllers.PostsController{}

app.Router.Get("/posts", posts.Index)
app.Router.Get("/posts/:id", posts.Show)
app.Router.Post("/posts", posts.Create)
app.Router.Put("/posts/:id", posts.Update)
app.Router.Delete("/posts/:id", posts.Delete)

Resourceful Routing Pattern

The pattern above follows RESTful conventions. Here is the full mapping for a resource:

  • GET /posts โ€” Index โ€” list all posts
  • GET /posts/:id โ€” Show โ€” show one post
  • POST /posts โ€” Create โ€” create a new post
  • PUT /posts/:id โ€” Update โ€” replace a post
  • DELETE /posts/:id โ€” Delete โ€” remove a post

Using Route Groups

Group controller routes under a shared prefix:

api := app.Router.Group("/api")

posts := &controllers.PostsController{}
api.Get("/posts", posts.Index)
api.Get("/posts/:id", posts.Show)
api.Post("/posts", posts.Create)
api.Put("/posts/:id", posts.Update)
api.Delete("/posts/:id", posts.Delete)

Passing Data to Views

Controllers can render .nimbus templates by calling c.View() with a data map:

func (pc *PostsController) Index(c *http.Context) error {
    posts := fetchPostsFromDB()
    return c.View("posts/index", map[string]any{
        "title": "All Posts",
        "posts": posts,
    })
}

The keys in the map become available as variables in your template. For example, title and posts can be referenced in the posts/index.nimbus view file.

Controller with Dependencies

Add fields to the controller struct for dependencies like a database connection:

type UsersController struct {
    DB *gorm.DB
}

func (uc *UsersController) Index(c *http.Context) error {
    var users []User
    uc.DB.Find(&users)
    return c.JSON(http.StatusOK, users)
}

// In main.go
users := &controllers.UsersController{DB: db}
app.Router.Get("/users", users.Index)