Annotations
Understanding @Router and @Provider annotations
Annotations
Taskw uses special annotations to understand your code structure and generate appropriate boilerplate. These annotations are Go comments that follow specific patterns.
Overview
Taskw recognizes one main type of annotation:
@Router
- Defines HTTP routes for handler functions
For dependency injection, Taskw automatically detects functions with the "Provide" prefix.
@Router Annotations
The @Router
annotation defines HTTP routes for your handler functions. It tells Taskw how to register routes with Fiber.
Basic Syntax
// @Router /path [method]
func (h *Handler) Method(c *fiber.Ctx) error {
// Implementation
}
Supported Format
Taskw supports the Swaggo specification format for @Router
annotations:
Swaggo Format
// @Router /users [get]
func (h *UserHandler) GetUsers(c *fiber.Ctx) error {
// Implementation
}
// @Router /users [post]
func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
// Implementation
}
// @Router /users/{id} [get]
func (h *UserHandler) GetUser(c *fiber.Ctx) error {
// Implementation
}
HTTP Methods
Taskw supports all standard HTTP methods:
// @Router /users [get]
func (h *UserHandler) GetUsers(c *fiber.Ctx) error { }
// @Router /users [post]
func (h *UserHandler) CreateUser(c *fiber.Ctx) error { }
// @Router /users/{id} [put]
func (h *UserHandler) UpdateUser(c *fiber.Ctx) error { }
// @Router /users/{id} [delete]
func (h *UserHandler) DeleteUser(c *fiber.Ctx) error { }
// @Router /users/{id} [patch]
func (h *UserHandler) PatchUser(c *fiber.Ctx) error { }
// @Router /health [head]
func (h *HealthHandler) HeadHealth(c *fiber.Ctx) error { }
// @Router /users [options]
func (h *UserHandler) OptionsUsers(c *fiber.Ctx) error { }
Path Parameters
Use curly braces {}
for path parameters:
// @Router GET /users/{id}
func (h *UserHandler) GetUser(c *fiber.Ctx) error {
id := c.Params("id")
// Implementation
}
// @Router PUT /users/{id}/profile
func (h *UserHandler) UpdateUserProfile(c *fiber.Ctx) error {
id := c.Params("id")
// Implementation
}
// @Router GET /orders/{orderId}/items/{itemId}
func (h *OrderHandler) GetOrderItem(c *fiber.Ctx) error {
orderId := c.Params("orderId")
itemId := c.Params("itemId")
// Implementation
}
Nested Routes
Organize routes hierarchically:
// @Router GET /api/v1/users
func (h *UserHandler) GetUsers(c *fiber.Ctx) error { }
// @Router GET /api/v1/users/{id}
func (h *UserHandler) GetUser(c *fiber.Ctx) error { }
// @Router POST /api/v1/users
func (h *UserHandler) CreateUser(c *fiber.Ctx) error { }
// @Router PUT /api/v1/users/{id}
func (h *UserHandler) UpdateUser(c *fiber.Ctx) error { }
// @Router DELETE /api/v1/users/{id}
func (h *UserHandler) DeleteUser(c *fiber.Ctx) error { }
Query Parameters
While not part of the annotation, you can access query parameters in your handlers:
// @Router GET /users
func (h *UserHandler) GetUsers(c *fiber.Ctx) error {
page := c.Query("page", "1")
limit := c.Query("limit", "10")
search := c.Query("search")
// Implementation
}
Provider Functions
Taskw automatically detects provider functions by looking for functions with the "Provide" prefix. These functions are used for dependency injection with Wire.
Basic Syntax
func ProvideHandler(deps *Dependencies) *Handler {
return &Handler{deps: deps}
}
Provider Function Patterns
Constructor Functions
func ProvideUserHandler(service *UserService) *UserHandler {
return &UserHandler{service: service}
}
func ProvideUserService(repo *UserRepository) *UserService {
return &UserService{repo: repo}
}
func ProvideUserRepository(db *gorm.DB) *UserRepository {
return &UserRepository{db: db}
}
Interface Implementations
func ProvideUserService(repo UserRepository) UserService {
return &userService{repo: repo}
}
func ProvideUserRepository(db *gorm.DB) UserRepository {
return &userRepository{db: db}
}
Configuration Providers
func ProvideConfig() *Config {
return &Config{
DatabaseURL: os.Getenv("DATABASE_URL"),
Port: os.Getenv("PORT"),
}
}
func ProvideDatabase(config *Config) (*gorm.DB, error) {
return gorm.Open(postgres.Open(config.DatabaseURL), &gorm.Config{})
}
Dependency Chain
Taskw automatically resolves dependency chains:
func ProvideUserHandler(service *UserService) *UserHandler {
return &UserHandler{service: service}
}
func ProvideUserService(repo *UserRepository) *UserService {
return &UserService{repo: repo}
}
func ProvideUserRepository(db *gorm.DB) *UserRepository {
return &UserRepository{db: db}
}
func ProvideDatabase(config *Config) (*gorm.DB, error) {
return gorm.Open(postgres.Open(config.DatabaseURL), &gorm.Config{})
}
Generated dependency injection:
// Generated dependencies_gen.go
var ProviderSet = wire.NewSet(
handlers.ProvideUserHandler,
services.ProvideUserService,
repositories.ProvideUserRepository,
ProvideDatabase,
)
Annotation Placement
Handler Annotations
Place @Router
annotations directly above handler methods:
type UserHandler struct {
service *UserService
}
// @Router GET /users
func (h *UserHandler) GetUsers(c *fiber.Ctx) error {
// Implementation
}
// @Router POST /users
func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
// Implementation
}
Provider Functions
Place provider functions with the "Provide" prefix:
func ProvideUserHandler(service *UserService) *UserHandler {
return &UserHandler{service: service}
}
func ProvideUserService(repo *UserRepository) *UserService {
return &UserService{repo: repo}
}
Validation Rules
Route Validation
Taskw validates route annotations:
- ✅ Valid HTTP methods:
GET
,POST
,PUT
,DELETE
,PATCH
,HEAD
,OPTIONS
- ✅ Valid path formats:
/path
,/path/{param}
,/path/{param}/subpath
- ❌ Invalid path formats:
/path/:param
(use{param}
instead)
Provider Validation
Taskw validates provider functions:
- ✅ Functions with "Provide" prefix
- ✅ Functions that return a concrete type
- ❌ Functions without "Provide" prefix
- ❌ Functions that return interfaces (unless explicitly supported)
Common Patterns
RESTful API Pattern
type UserHandler struct {
service *UserService
}
// @Router GET /users
func (h *UserHandler) GetUsers(c *fiber.Ctx) error { }
// @Router GET /users/{id}
func (h *UserHandler) GetUser(c *fiber.Ctx) error { }
// @Router POST /users
func (h *UserHandler) CreateUser(c *fiber.Ctx) error { }
// @Router PUT /users/{id}
func (h *UserHandler) UpdateUser(c *fiber.Ctx) error { }
// @Router DELETE /users/{id}
func (h *UserHandler) DeleteUser(c *fiber.Ctx) error { }
Service Layer Pattern
func ProvideUserHandler(service *UserService) *UserHandler {
return &UserHandler{service: service}
}
func ProvideUserService(repo *UserRepository) *UserService {
return &UserService{repo: repo}
}
func ProvideUserRepository(db *gorm.DB) *UserRepository {
return &UserRepository{db: db}
}
Health Check Pattern
type HealthHandler struct{}
// @Router /health [get]
func (h *HealthHandler) GetHealth(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"status": "ok"})
}
func ProvideHealthHandler() *HealthHandler {
return &HealthHandler{}
}
Error Handling
Invalid Annotations
Taskw reports annotation errors:
❌ Validation errors:
• UserHandler.GetUser: Invalid route path "/users/:id" (should use {id})
• Missing "Provide" prefix for NewOrderHandler
Missing Annotations
Taskw can detect missing annotations:
⚠️ Warnings:
• UserHandler.GetUser: Handler function found but no @Router annotation
• NewOrderHandler: Function found but no "Provide" prefix
Best Practices
Use Consistent Naming
// ✅ Consistent naming
// @Router GET /users
func (h *UserHandler) GetUsers(c *fiber.Ctx) error { }
// @Router GET /users/{id}
func (h *UserHandler) GetUser(c *fiber.Ctx) error { }
// ❌ Inconsistent naming
// @Router GET /users
func (h *UserHandler) ListUsers(c *fiber.Ctx) error { }
Group Related Routes
// Group user-related routes
// @Router GET /users
func (h *UserHandler) GetUsers(c *fiber.Ctx) error { }
// @Router GET /users/{id}
func (h *UserHandler) GetUser(c *fiber.Ctx) error { }
// @Router POST /users
func (h *UserHandler) CreateUser(c *fiber.Ctx) error { }
// Group order-related routes
// @Router GET /orders
func (h *OrderHandler) GetOrders(c *fiber.Ctx) error { }
// @Router GET /orders/{id}
func (h *OrderHandler) GetOrder(c *fiber.Ctx) error { }
Use Descriptive Provider Names
// ✅ Descriptive names
// @Provider
func NewUserHandler(service *UserService) *UserHandler { }
// @Provider
func NewUserService(repo *UserRepository) *UserService { }
// ❌ Unclear names
// @Provider
func NewHandler(deps *Deps) *Handler { }
Troubleshooting
Common Issues
Route not generated: Check @Router
annotation syntax and placement
Provider not found: Ensure function name starts with "Provide"
Invalid path format: Use {param}
instead of :param
Missing dependencies: Check provider function signatures
Debugging Commands
# Scan to see what's detected
taskw scan
# Check for validation errors
taskw scan 2>&1 | grep "❌"
# Preview generated code
taskw generate
Annotation Examples
Here are complete examples of well-annotated code:
User Handler
package user
import "github.com/gofiber/fiber/v2"
type UserHandler struct {
service *UserService
}
// @Router /users [get]
func (h *UserHandler) GetUsers(c *fiber.Ctx) error {
users, err := h.service.GetUsers()
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
return c.JSON(users)
}
// @Router /users/{id} [get]
func (h *UserHandler) GetUser(c *fiber.Ctx) error {
id := c.Params("id")
user, err := h.service.GetUser(id)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "User not found",
})
}
return c.JSON(user)
}
// @Router /users [post]
func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
var user User
if err := c.BodyParser(&user); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request body",
})
}
createdUser, err := h.service.CreateUser(&user)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
return c.Status(fiber.StatusCreated).JSON(createdUser)
}
func ProvideUserHandler(service *UserService) *UserHandler {
return &UserHandler{service: service}
}
Service Layer
package user
func ProvideUserService(repo *UserRepository) *UserService {
return &UserService{repo: repo}
}
type UserService struct {
repo *UserRepository
}
func (s *UserService) GetUsers() ([]User, error) {
return s.repo.FindAll()
}
func (s *UserService) GetUser(id string) (*User, error) {
return s.repo.FindByID(id)
}
func (s *UserService) CreateUser(user *User) (*User, error) {
return s.repo.Create(user)
}