A CLI tool for automatically generating Fiber routes and Wire dependency injection code from annotations. Taskw eliminates boilerplate and keeps your API handlers focused on business logic.
When building Go APIs, you typically write:
- Handler functions with route annotations based on Swaggo (
@Router /api/users [get]) - Provider functions for dependency injection (
func Provide<FUNCTION_NAME>()) - Boilerplate route registration (manually wiring routes to handlers)
- Boilerplate dependency wiring (manually connecting all providers)
Taskw automatically generates #3 and #4 by scanning your code for annotations and provider functions.
🎯 Focused Stack:
- Web Framework: Fiber v2 only
- Dependency Injection: Google Wire only
- Annotations: Swaggo (@Router) only
- Language: Go
🚀 What It Generates:
- Auto-route registration from
@Routerannotations - Wire provider sets from
Provide*functions - Type-safe dependency injection code
- Development-friendly watching and regeneration
go install github.com/nkaewam/taskw@latestDownload the latest binary for your platform from the releases page.
git clone https://github.com/nkaewam/taskw.git
cd taskw
go build -o taskw main.go
sudo mv taskw /usr/local/bin/taskw initThis creates a taskw.yaml config file:
version: "1.0"
project:
module: "github.com/user/your-go-api"
paths:
scan_dirs:
- "./internal/api"
- "./internal/handlers"
output_dir: "./internal/api"
generation:
routes:
enabled: true
output_file: "routes_gen.go"
dependencies:
enabled: true
output_file: "dependencies_gen.go"// internal/api/user/handler.go
package user
import "github.com/gofiber/fiber/v2"
type Handler struct {
service *Service
}
// ProvideHandler creates a new user handler
func ProvideHandler(service *Service) *Handler {
return &Handler{service: service}
}
// GetUsers retrieves all users
// @Summary Get all users
// @Description Get a list of all users
// @Tags users
// @Accept json
// @Produce json
// @Success 200 {array} User
// @Router /api/v1/users [get]
func (h *Handler) GetUsers(c *fiber.Ctx) error {
users, err := h.service.GetAll()
if err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(users)
}
// CreateUser creates a new user
// @Summary Create user
// @Description Create a new user
// @Tags users
// @Accept json
// @Produce json
// @Param user body CreateUserRequest true "User data"
// @Success 201 {object} User
// @Router /api/v1/users [post]
func (h *Handler) CreateUser(c *fiber.Ctx) error {
// Implementation here
return c.SendStatus(201)
}// internal/api/user/service.go
package user
type Service struct {
repo Repository
}
// ProvideService creates a new user service
func ProvideService(repo Repository) *Service {
return &Service{repo: repo}
}
func (s *Service) GetAll() ([]User, error) {
return s.repo.FindAll()
}taskw generateThis generates:
internal/api/routes_gen.go- Auto-route registrationinternal/api/dependencies_gen.go- Wire provider sets
// cmd/server/main.go
package main
import (
"github.com/gofiber/fiber/v2"
"your-module/internal/api"
)
func main() {
app := fiber.New()
// Wire generates this function
server, cleanup, err := api.InitializeServer()
if err != nil {
panic(err)
}
defer cleanup()
// Auto-generated route registration
if err := server.RegisterRoutes(app); err != nil {
panic(err)
}
app.Listen(":8080")
}# Initialize taskw in existing project
taskw init
# Generate all code
taskw generate
# Generate specific components
taskw generate routes # Only route registration
taskw generate deps # Only dependency injection
# Watch for changes during development
taskw watch
# Debug what Taskw finds
taskw scan # Show discovered handlers and providers# taskw.yaml
version: "1.0"
# Project information
project:
module: "github.com/user/my-api" # Go module from go.mod
# File paths
paths:
scan_dirs: # Directories to scan
- "./internal/api"
- "./internal/handlers"
- "./pkg/handlers"
output_dir: "./internal/api" # Where to write generated files
# Code generation settings
generation:
routes:
enabled: true
output_file: "routes_gen.go" # Generated route registration
dependencies:
enabled: true
output_file: "deps_gen.go" # Generated Wire providersTaskw scans for:
- @Router annotations in comments above handler methods
- Provide* functions that return handler types
Generated code:
// routes_gen.go (generated)
func (s *Server) RegisterRoutes(app *fiber.App) {
app.Get("/api/v1/users", s.userHandler.GetUsers)
app.Post("/api/v1/users", s.userHandler.CreateUser)
// ... more routes
}Taskw scans for:
- Provider functions starting with
Provide* - Return types and parameter dependencies
Generated code:
// deps_gen.go (generated)
var ProviderSet = wire.NewSet(
user.ProvideHandler,
user.ProvideService,
user.ProvideRepository,
// ... more providers
)version: "3"
tasks:
generate:
desc: Generate all code
cmds:
- taskw generate
- go generate ./...
dev:
desc: Start development server
deps: [generate]
cmds:
- air
build:
desc: Build application
deps: [generate]
cmds:
- go build -o bin/server cmd/server/main.go.PHONY: generate dev build
generate:
taskw generate
go generate ./...
dev: generate
air
build: generate
go build -o bin/server cmd/server/main.go#!/bin/sh
# .git/hooks/pre-commit
taskw generate
git add internal/api/*_gen.gomy-api/
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── api/
│ │ ├── server.go
│ │ ├── wire.go # Manual Wire config
│ │ ├── routes_gen.go # Generated by Taskw
│ │ ├── deps_gen.go # Generated by Taskw
│ │ └── wire_gen.go # Generated by Wire
│ ├── user/
│ │ ├── handler.go # Has @Router + ProvideHandler
│ │ ├── service.go # Has ProvideService
│ │ └── repository.go # Has ProvideRepository
│ └── product/
│ ├── handler.go
│ ├── service.go
│ └── repository.go
├── taskw.yaml # Taskw config
├── Taskfile.yml # Build config
└── go.mod
// internal/api/wire.go
//go:build wireinject
package api
import (
"github.com/google/wire"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
// ProviderSet combines manual + generated providers
var ProviderSet = wire.NewSet(
// Manual providers
provideLogger,
provideFiberApp,
// Generated providers (from deps_gen.go)
GeneratedProviderSet,
// Server
NewServer,
)
func InitializeServer() (*Server, func(), error) {
wire.Build(ProviderSet)
return &Server{}, nil, nil
}
func provideLogger() *zap.Logger {
logger, _ := zap.NewProduction()
return logger
}
func provideFiberApp() *fiber.App {
return fiber.New()
}If you already have manual route/DI code:
taskw init// Before
func (h *UserHandler) GetUsers(c *fiber.Ctx) error { ... }
// After
// @Router /api/v1/users [get]
func (h *UserHandler) GetUsers(c *fiber.Ctx) error { ... }// Add to existing files
func ProvideUserHandler(service *UserService) *UserHandler {
return &UserHandler{service: service}
}taskw generate
# Replace manual route registration with generated version
# Replace manual Wire providers with generated version# Regenerate on file changes
taskw watch
# Combine with Air for live reload
air &
taskw watch &# See what Taskw discovers
taskw scan
# Output:
# 📁 Scanning ./internal/api
# 🔍 Found handlers:
# - user.ProvideHandler -> *user.Handler
# - product.ProvideHandler -> *product.Handler
# 🔍 Found routes:
# - GetUsers: GET /api/v1/users
# - CreateUser: POST /api/v1/users
# 📁 Scanning ./internal/handlers
# (no handlers found)Taskw doesn't find my handlers
- Check
scan_dirsintaskw.yaml - Ensure files end with
handler.go - Ensure functions start with
Provide*
Generated routes don't work
- Check @Router annotation syntax
- Ensure handler methods match
func (h *Handler) Method(c *fiber.Ctx) error
Wire compilation fails
- Check provider function signatures
- Ensure all dependencies have providers
- Run
go generate ./...aftertaskw generate
Generated code has wrong imports
- Check
project.moduleintaskw.yamlmatchesgo.mod
# Verbose mode
taskw generate --verbose
# Show generated file contents
taskw generate --dry-runv1.1
- Watch mode (
taskw watch) - Template customization
- Better error messages
v2.0
- Support for Gin framework
- Support for Fx dependency injection
- Plugin system
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
MIT License - see LICENSE file for details.
Taskw - From manual boilerplate to auto-generated bliss 🚀