Back
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

  1. Handler - HTTP layer, handles requests/responses
  2. Service - Business logic layer
  3. Repository - Database operations layer
  4. 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) error
  • GetByID(id int) (*models.User, error)
  • GetAll() ([]models.User, error)
  • Update(user *models.User) error
  • Delete(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?

LayerResponsibility
HandlerHTTP concerns (JSON, status codes)
ServiceBusiness rules & orchestration
RepositoryData persistence
ModelsShared data structures

This separation makes testing easier and code more maintainable.

API Endpoints

  • GET /users - List all users
  • GET /users/{id} - Get specific user
  • POST /users - Create user
  • PUT /users/{id} - Update user
  • DELETE /users/{id} - Delete user

Lessons Learned

  1. Avoid circular imports - Put shared structs in a models package
  2. SQLite is great for simple APIs - No external DB server needed
  3. Go’s stdlib is powerful - http.ServeMux works fine for small APIs
  4. 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! 🦀