Controllers
Controllers are responsible for handling incoming requests and returning responses to the client. In Nika, controllers use a struct-tag routing approach where routes are defined directly on exported function fields.
Route Definition
Routes are defined using the route struct tag in the format METHOD:path:
type UserController struct {
service *UserService
}
func NewUserController(service *UserService) *UserController {
return &UserController{service: service}
}
Route Handler Functions
Each exported function field in the controller struct can be tagged as a route:
func NewUserController(service *UserService) *UserController {
ctrl := &UserController{service: service}
ctrl.List = func(c *gin.Context) {
users := ctrl.service.FindAll()
c.JSON(http.StatusOK, users)
}
ctrl.Create = func(c *gin.Context) {
// handler logic
}
ctrl.FindOne = func(c *gin.Context) {
id := c.Param("id")
user, err := ctrl.service.FindByID(id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
c.JSON(http.StatusOK, user)
}
return ctrl
}
Supported HTTP Methods
| Method | Tag Example | Description |
|---|---|---|
GET |
route:"GET:/users" |
Retrieve resources |
POST |
route:"POST:/users" |
Create a resource |
PUT |
route:"PUT:/users/:id" |
Replace a resource |
PATCH |
route:"PATCH:/users/:id" |
Partially update a resource |
DELETE |
route:"DELETE:/users/:id" |
Delete a resource |
OPTIONS |
route:"OPTIONS:/users" |
CORS preflight |
Full Example
Here is a complete controller with multiple routes:
package src
import (
"net/http"
"github.com/gin-gonic/gin"
)
type UserController struct {
service *UserService
List func(*gin.Context) `route:"GET:/users"`
Create func(*gin.Context) `route:"POST:/users"`
FindOne func(*gin.Context) `route:"GET:/users/:id"`
Update func(*gin.Context) `route:"PUT:/users/:id"`
Delete func(*gin.Context) `route:"DELETE:/users/:id"`
}
func NewUserController(service *UserService) *UserController {
ctrl := &UserController{service: service}
ctrl.List = func(c *gin.Context) {
users := ctrl.service.FindAll()
c.JSON(http.StatusOK, users)
}
ctrl.Create = func(c *gin.Context) {
var dto CreateUserDTO
if !validator.BindAndValidate(c, &dto) {
return
}
user := ctrl.service.Create(dto)
response.Create(c, user)
}
ctrl.FindOne = func(c *gin.Context) {
id := c.Param("id")
user := ctrl.service.FindOne(id)
if user == nil {
response.NotFoundRequest(c, "USER_NOT_FOUND", "User not found", nil)
return
}
response.Ok(c, user)
}
ctrl.Update = func(c *gin.Context) {
id := c.Param("id")
var dto UpdateUserDTO
if !validator.BindAndValidate(c, &dto) {
return
}
ctrl.service.Update(id, dto)
response.OkByMsg(c, "User updated successfully")
}
ctrl.Delete = func(c *gin.Context) {
id := c.Param("id")
ctrl.service.Delete(id)
response.OkByMsg(c, "User deleted successfully")
}
return ctrl
}
Dependency Injection in Controllers
Controllers are automatically resolved by Nika's DI container. When you register a controller as a constructor function, Nika resolves all dependencies from the container:
// Constructor returns *UserController — Nika auto-resolves *UserService
func NewUserController(service *UserService) *UserController {
// ...
}
// Register in module
func (m *AppModule) Controllers() []interface{} {
return []interface{}{
NewUserController, // Pass the constructor, not an instance
}
}
Registration
Register your controllers in a module's Controllers() method:
func (m *AppModule) Controllers() []interface{} {
return []interface{}{
NewUserController,
NewAuthController,
}
}
When the application starts, Nika will:
- Call
NewUserControllerand resolve*UserServicefrom the DI container - Scan the struct fields for
routetags - Register each tagged function as a Gin route
✅ Registered: GET /users -> List
✅ Registered: POST /users -> Create
✅ Registered: GET /users/:id -> FindOne
✅ Registered: PUT /users/:id -> Update
✅ Registered: DELETE /users/:id -> Delete
Note
Route handler fields must be exported (start with an uppercase letter) and must be of type func(*gin.Context).