🌐 Project Goal#
Build a simple web service using the Gin
framework.
🛠️ Project Structure#
1todo-api/
2├── main.go # Entrypoint file
3├── routes/
4│ └── todo_routes.go # Route definitions
5├── models/
6│ └── todo.go # Data structure
7├── middleware/
8│ └── logging.go # Custom middleware
9├── config/
10│ └── config.go # Configuration management
11└── go.mod # Go module
📦 Install Dependencies#
1go mod init todo-api
2go get -u github.com/gin-gonic/gin
3go get -u github.com/jackc/pgx/v4
🧪 Code Examples#
1. Configuration Management (config/config.go
)#
1package config
2
3import "github.com/joho/godotenv"
4
5func LoadEnv() {
6 err := godotenv.Load()
7 if err != nil {
8 panic("Error loading .env file")
9 }
10}
2. Database Connection (main.go
)#
1package main
2
3import (
4 "context"
5 "fmt"
6 "log"
7 "os" // Make sure to import os to use os.Getenv()
8 "github.com/gin-gonic/gin"
9 "github.com/jackc/pgx/v4"
10 "todo-api/config"
11 "todo-api/routes"
12)
13
14type Todo struct {
15 ID int `json:"id"`
16 Title string `json:"title"`
17}
18
19func main() {
20 // 1. Load environment variables
21 config.LoadEnv()
22
23 // 2. Connect to PostgreSQL
24 connStr := fmt.Sprintf(
25 "postgres://%s:%s@%s:%s/%s?sslmode=disable",
26 os.Getenv("DB_USER"),
27 os.Getenv("DB_PASSWORD"),
28 os.Getenv("DB_HOST"),
29 os.Getenv("DB_PORT"),
30 os.Getenv("DB_NAME"),
31 )
32
33 conn, err := pgx.Connect(context.Background(), connStr)
34 if err != nil {
35 log.Fatal("Unable to connect to database:", err)
36 }
37 defer conn.Close(context.Background())
38
39 // 3. Create Gin application
40 r := gin.Default()
41
42 // 4. Register routes
43 todoRoutes := routes.TodoRoutes{DB: conn}
44 r.POST("/todos", todoRoutes.CreateTodo)
45 r.GET("/todos", todoRoutes.GetAllTodos)
46
47 // 5. Start the server
48 r.Run(":8080")
49}
3. Route Implementation (routes/todo_routes.go
)#
1package routes
2
3import (
4 "context" // Make sure to import context
5 "github.com/gin-gonic/gin"
6 "github.com/jackc/pgx/v4" // Make sure to import pgx
7 "todo-api/models"
8)
9
10type TodoRoutes struct {
11 DB *pgx.Conn
12}
13
14func (tr *TodoRoutes) CreateTodo(c *gin.Context) {
15 var todo models.Todo
16 if err := c.ShouldBindJSON(&todo); err != nil {
17 c.JSON(400, gin.H{"error": err.Error()})
18 return
19 }
20
21 _, err := tr.DB.Exec(context.Background(), "INSERT INTO todos (title) VALUES ($1)", todo.Title)
22 if err != nil {
23 c.JSON(500, gin.H{"error": "Database operation failed"})
24 return
25 }
26
27 c.JSON(201, gin.H{"message": "Successfully created task"})
28}
29
30func (tr *TodoRoutes) GetAllTodos(c *gin.Context) {
31 rows, err := tr.DB.Query(context.Background(), "SELECT id, title FROM todos")
32 if err != nil {
33 c.JSON(500, gin.H{"error": "Query failed"})
34 return
35 }
36 defer rows.Close() // Good practice to close rows
37
38 var todos []models.Todo
39 for rows.Next() {
40 var t models.Todo
41 if err := rows.Scan(&t.ID, &t.Title); err != nil {
42 c.JSON(500, gin.H{"error": "Failed to parse results"})
43 return
44 }
45 todos = append(todos, t)
46 }
47
48 c.JSON(200, todos)
49}