2026-02-22 • blog
Building Simple REST API with Golang and SQLite
Introduction
Building a REST API in Go doesn’t have to be complicated. In this post, I’ll walk you through creating a simple yet well-structured API using Golang, SQLite, and a clean architecture pattern with handlers, services, and repositories.
Project Structure
simple_api/
├── main.go
├── go.mod
├── handler/
│ └── user_handler.go
├── service/
│ └── user_service.go
├── repositories/
│ └── user_repository.go
├── models/
│ └── user.go
└── app.db
The Pattern
- Handler - HTTP layer, handles requests/responses
- Service - Business logic layer
- Repository - Database operations layer
- Models - Data structures shared across layers
Key Code Snippets
Models (models/user.go)
package models
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
Repository (repositories/user_repository.go)
Handles all database operations with SQLite:
Create(user *models.User) errorGetByID(id int) (*models.User, error)GetAll() ([]models.User, error)Update(user *models.User) errorDelete(id int) error
Service (service/user_service.go)
Business logic layer - calls repository methods and can add validation/rules.
Handler (handler/user_handler.go)
HTTP handlers using http.ServeMux - routes requests to service methods.
Main (main.go)
func main() {
// Setup dependencies
repo := repositories.NewUserRepository()
svc := service.NewUserService(repo)
handler := handler.NewUserHandler(svc)
// Router
mux := http.NewServeMux()
mux.HandleFunc("/users", handler.HandleUsers)
mux.HandleFunc("/users/", handler.HandleUser)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}
Why This Pattern?
| Layer | Responsibility |
|---|---|
| Handler | HTTP concerns (JSON, status codes) |
| Service | Business rules & orchestration |
| Repository | Data persistence |
| Models | Shared data structures |
This separation makes testing easier and code more maintainable.
API Endpoints
GET /users- List all usersGET /users/{id}- Get specific userPOST /users- Create userPUT /users/{id}- Update userDELETE /users/{id}- Delete user
Lessons Learned
- Avoid circular imports - Put shared structs in a
modelspackage - SQLite is great for simple APIs - No external DB server needed
- Go’s stdlib is powerful -
http.ServeMuxworks fine for small APIs - Dependency injection - Pass interfaces, not concrete types
Conclusion
This simple pattern scales well for small to medium projects. The clean separation means you can swap SQLite for PostgreSQL later, or add caching in the service layer without touching handlers.
Full code available in my repo. Happy coding! 🦀