Quick Start
Build your first API with Taskw in 5 minutes - from zero to working endpoints
Quick Start
Build a working Go API with automatic route registration and dependency injection in just 5 minutes.
What you'll build: A simple user management API with GET and POST endpoints, complete with dependency injection and automatic route registration.
Prerequisites
- Taskw installed on your system
- Go 1.21+ installed
- Basic knowledge of Go and HTTP APIs
Step-by-Step Tutorial
Create a new Go project
mkdir my-api && cd my-api
go mod init github.com/yourname/my-api
Initialize Taskw
taskw init github.com/yourname/my-api
This creates a complete project structure:
cmd/server/main.go
- Main server entry pointinternal/api/server.go
- Server struct and providersinternal/api/wire.go
- Wire dependency injection setupinternal/health/handler.go
- Example health check handlertaskw.yaml
- Taskw configurationTaskfile.yml
- Build automation.air.toml
- Live reload configuration
Create a user model
Create internal/models/user.go
:
package models
// User represents a user in our system
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// CreateUserRequest represents the request body for creating a user
type CreateUserRequest struct {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
}
Create a user repository
Create internal/user/repository.go
:
package user
import (
"github.com/yourname/my-api/internal/models"
)
type Repository struct {
users []models.User
nextID int
}
// ProvideRepository creates a new user repository
func ProvideRepository() *Repository {
return &Repository{
users: []models.User{
{ID: 1, Name: "John Doe", Email: "john@example.com"},
{ID: 2, Name: "Jane Smith", Email: "jane@example.com"},
},
nextID: 3,
}
}
func (r *Repository) FindAll() []models.User {
return r.users
}
func (r *Repository) Create(req models.CreateUserRequest) models.User {
user := models.User{
ID: r.nextID,
Name: req.Name,
Email: req.Email,
}
r.users = append(r.users, user)
r.nextID++
return user
}
Create a user service
Create internal/user/service.go
:
package user
import (
"github.com/yourname/my-api/internal/models"
)
type Service struct {
repo *Repository
}
// ProvideService creates a new user service
func ProvideService(repo *Repository) *Service {
return &Service{repo: repo}
}
func (s *Service) GetAll() []models.User {
return s.repo.FindAll()
}
func (s *Service) Create(req models.CreateUserRequest) models.User {
return s.repo.Create(req)
}
Create a user handler with routes
Create internal/user/handler.go
:
package user
import (
"github.com/gofiber/fiber/v2"
"github.com/yourname/my-api/internal/models"
)
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 in the system
// @Tags users
// @Accept json
// @Produce json
// @Success 200 {array} models.User
// @Router /api/v1/users [get]
func (h *Handler) GetUsers(c *fiber.Ctx) error {
users := h.service.GetAll()
return c.JSON(fiber.Map{
"data": users,
"count": len(users),
})
}
// CreateUser creates a new user
// @Summary Create a new user
// @Description Add a new user to the system
// @Tags users
// @Accept json
// @Produce json
// @Param user body models.CreateUserRequest true "User data"
// @Success 201 {object} models.User
// @Failure 400 {object} map[string]string
// @Router /api/v1/users [post]
func (h *Handler) CreateUser(c *fiber.Ctx) error {
var req models.CreateUserRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
}
if req.Name == "" || req.Email == "" {
return c.Status(400).JSON(fiber.Map{"error": "Name and email are required"})
}
user := h.service.Create(req)
return c.Status(201).JSON(user)
}
Notice the @Router
annotations! These tell Taskw which HTTP methods and paths to map to your handler functions.
Generate the boilerplate code
Run Taskw to generate route registration and dependency injection:
taskw generate
You should see output like:
🔄 Generating routes...
✅ Generated routes in internal/api/routes_gen.go
🔄 Generating dependencies...
✅ Generated dependencies in internal/api/dependencies_gen.go
Install dependencies and generate Wire code
go mod tidy
go generate ./...
Run your API
# Using Taskfile (recommended)
task dev
# Or directly with go run
go run cmd/server/main.go
Your API is now running at http://localhost:8080
!
Test your API
Get all users:
curl http://localhost:8080/api/v1/users
Expected response:
{
"data": [
{"id": 1, "name": "John Doe", "email": "john@example.com"},
{"id": 2, "name": "Jane Smith", "email": "jane@example.com"}
],
"count": 2
}
Create a new user:
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"name": "Bob Johnson", "email": "bob@example.com"}'
Expected response:
{
"id": 3,
"name": "Bob Johnson",
"email": "bob@example.com"
}
Check Swagger docs:
Open http://localhost:8080/docs/
in your browser to see auto-generated API documentation.
What Just Happened?
Let's understand what Taskw generated for you:
1. Route Registration (internal/api/routes_gen.go
)
// Generated by Taskw - DO NOT EDIT
func (s *Server) RegisterRoutes(app *fiber.App) error {
app.Get("/api/v1/users", s.userHandler.GetUsers)
app.Post("/api/v1/users", s.userHandler.CreateUser)
app.Get("/health", s.healthHandler.Health)
return nil
}
2. Dependency Injection (internal/api/dependencies_gen.go
)
// Generated by Taskw - DO NOT EDIT
var GeneratedProviderSet = wire.NewSet(
user.ProvideHandler,
user.ProvideService,
user.ProvideRepository,
health.ProvideHandler,
)
3. The Magic
When you added @Router
annotations to your handlers, Taskw:
- Scanned your code for these annotations
- Extracted the HTTP method and path information
- Generated the route registration code automatically
When you created Provide*
functions, Taskw:
- Found all functions starting with "Provide"
- Analyzed their return types and dependencies
- Generated the Wire provider set automatically
Next Steps
🎉 Congratulations! You've built a working API with Taskw. Here's what to explore next:
Common Next Questions
Q: How do I add authentication?
A: TBA
Q: Can I customize the generated code?
A: The generated code shouldn't be modified directly. Customize through configuration and provider functions.