On this page
Final Project: CLI Task Manager with HTTP API
Final Project: CLI Task Manager with HTTP API
Congratulations on reaching the final lesson! You have covered the full breadth of modern Go — from basic types and control flow to structs, interfaces, goroutines, channels, error handling, modules, and HTTP. Now it is time to consolidate everything by building a real, complete project from scratch.
What You Will Build
A task manager that operates in two modes:
- CLI mode: interact from the command line (
list,create,complete,delete) - HTTP server mode: exposes a REST API on
localhost:8080
Data is persisted to a JSON file, concurrency is protected with sync.RWMutex, and the server shuts down gracefully by capturing OS signals.
Project Architecture
go-tasks/
├── go.mod
└── main.go ← everything in one file for this projectThe design follows these Go principles:
Repositoryinterface: decouples business logic from the storage implementationJSONRepostruct: concrete implementation with file-based persistence- Thread-safe concurrency:
sync.RWMutexprotects the task map - Graceful shutdown: the HTTP server listens for OS signals to close cleanly
- Explicit error handling: every function returns
error; sentinel errors enable comparison witherrors.Is
Applied Concepts
1. Interfaces for Decoupling
The Repository interface defines the contract that any storage implementation must fulfill. The CLI and server work exclusively with this interface:
type Repository interface {
Create(title, description string, priority int) (*Task, error)
List() ([]*Task, error)
GetByID(id int) (*Task, error)
Update(task *Task) error
Delete(id int) error
}To add PostgreSQL storage, you would only need to create a PostgresRepo struct implementing the same 5 methods.
2. Concurrency with sync.RWMutex
The HTTP server receives multiple concurrent requests. The task map must be protected:
// Multiple concurrent reads are allowed
func (r *JSONRepo) List() ([]*Task, error) {
r.mu.RLock() // multiple goroutines can read simultaneously
defer r.mu.RUnlock()
// ...
}
// Only one write at a time
func (r *JSONRepo) Create(...) (*Task, error) {
r.mu.Lock() // blocks everything: readers and writers
defer r.mu.Unlock()
// ...
}3. ServeMux Pattern Matching (Go 1.22+)
mux.HandleFunc("GET /api/tasks", list)
mux.HandleFunc("POST /api/tasks", create)
mux.HandleFunc("GET /api/tasks/{id}", getByID)
mux.HandleFunc("DELETE /api/tasks/{id}", delete)The HTTP method in the pattern ensures that GET /api/tasks and POST /api/tasks route to different handlers automatically.
4. Graceful Shutdown with Channels and Select
signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
errCh := make(chan error, 1)
go func() {
errCh <- srv.ListenAndServe()
}()
select {
case err := <-errCh:
// Server terminated due to an error
case <-signals:
// Ctrl+C or SIGTERM: shut down gracefully
srv.Shutdown(ctx)
}How to Run the Project
# Initialize the module
go mod init github.com/yourusername/go-tasks
# List tasks
go run main.go list
# Create a task
go run main.go create --title "Study Go" --desc "Complete the course" --priority 3
# Complete a task
go run main.go complete 1
# Start the HTTP server
go run main.go server
# In another terminal, test the API
curl http://localhost:8080/api/tasks
curl -X POST http://localhost:8080/api/tasks \
-H "Content-Type: application/json" \
-d '{"title":"New task","priority":2}'
curl http://localhost:8080/api/tasks/1
curl -X DELETE http://localhost:8080/api/tasks/1Possible Extensions
Once the base project works, you can extend it with:
- List filters:
GET /api/tasks?status=pending&priority=3 - Basic authentication: middleware that verifies an API key in the header
- Pagination:
GET /api/tasks?page=2&per_page=10 - Due dates: a
DueAt *time.Timefield inTask - Notifications: a goroutine that checks for overdue tasks and notifies
- SQLite: implement
SQLiteRepowithdatabase/sqland themodernc.org/sqlitedriver - Tests:
InMemoryRepofor unit tests without files
What You Learned in This Course
With this project you complete your mastery of essential Go:
- Static, strong, expressive type system
- Functions with multiple returns and the
(value, error)pattern - Structs, methods, and implicit interfaces
- Slices and maps with their specific idioms
- Safe pointers without pointer arithmetic
- Explicit error handling with
errors.Isanderrors.As - Goroutines — millions possible at 2-4 KB each
- Channels for safe communication between goroutines
- Concurrency patterns: worker pool, fan-in/out, pipeline
context.Contextfor propagated cancellation- Module and package system
- REST APIs with
net/httpand Go 1.22+ServeMux
Go is the language of modern infrastructure. Docker, Kubernetes, Terraform, Prometheus — all born in Go. With this course you have the foundation to build tools of the same caliber.
The natural next steps are to explore the Go ecosystem: gorm or sqlx for databases, gin or chi for advanced routing, and testify for more expressive testing.
Sign in to track your progress