Build web application with Golang
The “Build web application with Golang” library is a comprehensive ebook and learning resource that teaches developers how to build web applications using the Go programming language. With an impressive 44,185 stars on GitHub, this resource has become one of the most popular guides for Go web development. Created by Asta Xie, this book provides a thorough introduction to web development concepts, HTTP servers, routing, middleware, database integration, and modern web application architecture patterns using Go.
Developers should care about this library because it bridges the gap between learning Go syntax and building production-ready web applications. The resource covers everything from basic HTTP handling to advanced topics like WebSocket communication, RESTful API design, and deployment strategies. Whether you’re a beginner learning Go or an experienced developer transitioning from other languages, this guide provides practical examples and best practices that are immediately applicable to real-world projects.
Real-world use cases include building REST APIs for microservices, creating full-stack web applications, implementing WebSocket-based real-time features, and developing high-performance web servers that can handle thousands of concurrent connections. The problems it solves include understanding Go’s net/http package, implementing proper request routing, handling authentication and authorization, integrating with databases, and structuring web applications for maintainability and scalability.
Key Features
The library covers several key features that make Go an excellent choice for web development:
Comprehensive HTTP Server Implementation - The guide thoroughly explains how to use Go’s net/http package to create robust HTTP servers. It covers request handling, response writing, middleware implementation, and server configuration. The resource shows how to handle different HTTP methods, manage request headers, and implement proper error handling for production environments.
Advanced Routing Systems - Beyond basic HTTP handling, the library demonstrates how to implement sophisticated routing systems using third-party routers like Gorilla Mux or Chi. These routers provide features like path parameters, query string parsing, route groups, and middleware chaining that are essential for building complex web applications.
Database Integration Patterns - The guide covers various database integration approaches, including SQL databases using database/sql, NoSQL databases like MongoDB, and Redis for caching. It explains connection pooling, transaction management, and ORM usage patterns that are crucial for data persistence in web applications.
WebSocket Communication - Modern web applications often require real-time communication, and the library provides detailed examples of implementing WebSocket servers in Go. It covers connection management, message broadcasting, and handling different WebSocket events, enabling developers to build chat applications, live dashboards, and collaborative tools.
Authentication and Authorization - Security is a critical aspect of web development, and the resource covers various authentication methods including JWT tokens, OAuth integration, and session management. It explains how to implement middleware for protecting routes, handling user authentication flows, and securing API endpoints.
Template Rendering and Static File Serving - The library demonstrates how to use Go’s html/template package for server-side rendering, including template inheritance, custom functions, and security best practices. It also covers serving static files efficiently and implementing content delivery strategies.
Installation and Setup
The “Build web application with Golang” library is primarily a learning resource and ebook, so there’s no traditional installation process. However, to follow along with the examples and build the applications described in the guide, you’ll need to set up a Go development environment:
1# Ensure you have Go installed (version 1.16 or higher recommended)
2go version
3
4# Clone the repository to access all examples and source code
5git clone https://github.com/astaxie/build-web-application-with-golang.git
6cd build-web-application-with-golang
7
8# Navigate to the appropriate chapter directory for examples
9# For instance, to access chapter 4 examples:
10cd examples/chapter4
The resource requires Go 1.16 or higher for most examples, though some specific features might require newer versions. No additional dependencies are required for the basic examples, but some advanced chapters may require third-party libraries like Gorilla Mux for routing or specific database drivers.
To verify your setup, you can run one of the basic examples:
1cd examples/chapter4/1
2go run main.go
This should start a simple HTTP server that you can access at http://localhost:8080, confirming that your Go environment is properly configured to run the examples.
Basic Usage
Here’s a minimal “Hello World” example that demonstrates the basic structure of a Go web application:
1package main
2
3import (
4 "fmt"
5 "net/http"
6)
7
8func sayHello(w http.ResponseWriter, r *http.Request) {
9 fmt.Fprintf(w, "Hello, World!")
10}
11
12func main() {
13 http.HandleFunc("/", sayHello)
14
15 fmt.Println("Server starting on port 8080...")
16 err := http.ListenAndServe(":8080", nil)
17 if err != nil {
18 fmt.Printf("Server failed to start: %s\n", err)
19 }
20}
This example demonstrates the fundamental pattern used throughout Go web development. The sayHello function is a handler that takes an http.ResponseWriter and *http.Request as parameters. It uses fmt.Fprintf to write a simple “Hello, World!” response to the client.
In the main function, http.HandleFunc registers the handler function for the root path ("/"). The http.ListenAndServe function starts the HTTP server on port 8080. The second parameter is nil, which means it uses the default ServeMux (multiplexer) that routes incoming requests to the appropriate handlers.
When you run this program with go run main.go, it will start an HTTP server that listens on port 8080. You can then open your browser and navigate to http://localhost:8080 to see the “Hello, World!” message. The server will continue running until you stop it (usually with Ctrl+C).
The expected output in your terminal will be:
1Server starting on port 8080...
And visiting http://localhost:8080 in your browser will display:
1Hello, World!
Real-World Examples
Example 1: REST API Server with Middleware and Database Integration
1package main
2
3import (
4 "context"
5 "database/sql"
6 "encoding/json"
7 "fmt"
8 "log"
9 "net/http"
10 "os"
11 "time"
12
13 _ "github.com/lib/pq"
14 "github.com/gorilla/mux"
15)
16
17// User represents a user in the system
18type User struct {
19 ID string `json:"id"`
20 Name string `json:"name"`
21 Email string `json:"email"`
22 CreatedAt time.Time `json:"created_at"`
23}
24
25// Database connection pool
26var db *sql.DB
27
28// Connect to PostgreSQL database
29func connectDB() error {
30 var err error
31 connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
32 os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USER"),
33 os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME"))
34
35 db, err = sql.Open("postgres", connStr)
36 if err != nil {
37 return err
38 }
39
40 // Test the connection
41 err = db.Ping()
42 if err != nil {
43 return err
44 }
45
46 // Configure connection pool
47 db.SetMaxOpenConns(25)
48 db.SetMaxIdleConns(25)
49 db.SetConnMaxLifetime(5 * time.Minute)
50
51 log.Println("Successfully connected to database")
52 return nil
53}
54
55// Middleware for logging requests
56func loggingMiddleware(next http.Handler) http.Handler {
57 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
58 log.Printf("%s %s %s", r.Method, r.RequestURI, r.RemoteAddr)
59 next.ServeHTTP(w, r)
60 })
61}
62
63// Middleware for CORS
64func corsMiddleware(next http.Handler) http.Handler {
65 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
66 w.Header().Set("Access-Control-Allow-Origin", "*")
67 w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
68 w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
69
70 if r.Method == "OPTIONS" {
71 w.WriteHeader(http.StatusOK)
72 return
73 }
74
75 next.ServeHTTP(w, r)
76 })
77}
78
79// Get all users
80func getUsers(w http.ResponseWriter, r *http.Request) {
81 ctx := context.Background()
82 rows, err := db.QueryContext(ctx, "SELECT id, name, email, created_at FROM users")
83 if err != nil {
84 http.Error(w, err.Error(), http.StatusInternalServerError)
85 return
86 }
87 defer rows.Close()
88
89 var users []User
90 for rows.Next() {
91 var user User
92 err := rows.Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt)
93 if err != nil {
94 http.Error(w, err.Error(), http.StatusInternalServerError)
95 return
96 }
97 users = append(users, user)
98 }
99
100 w.Header().Set("Content-Type", "application/json")
101 json.NewEncoder(w).Encode(users)
102}
103
104// Create a new user
105func createUser(w http.ResponseWriter, r *http.Request) {
106 var user User
107 err := json.NewDecoder(r.Body).Decode(&user)
108 if err != nil {
109 http.Error(w, err.Error(), http.StatusBadRequest)
110 return
111 }
112
113 ctx := context.Background()
114 err = db.QueryRowContext(ctx, "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, created_at",
115 user.Name, user.Email).Scan(&user.ID, &user.CreatedAt)
116 if err != nil {
117 http.Error(w, err.Error(), http.StatusInternalServerError)
118 return
119 }
120
121 w.Header().Set("Content-Type", "application/json")
122 w.WriteHeader(http.StatusCreated)
123 json.NewEncoder(w).Encode(user)
124}
125
126func main() {
127 // Connect to database
128 err := connectDB()
129 if err != nil {
130 log.Fatalf("Failed to connect to database: %s", err)
131 }
132 defer db.Close()
133
134 // Setup router
135 r := mux.NewRouter()
136 r.Use(loggingMiddleware)
137 r.Use(corsMiddleware)
138
139 // API routes
140 r.HandleFunc("/api/users", getUsers).Methods("GET")
141 r.HandleFunc("/api/users", createUser).Methods("POST")
142
143 // Start server
144 port := ":8080"
145 log.Printf("Server starting on port %s...\n", port)
146 err = http.ListenAndServe(port, r)
147 if err != nil {
148 log.Fatalf("Server failed to start: %s", err)
149 }
150}
This example demonstrates a complete REST API server with database integration, middleware, and proper error handling. The server uses PostgreSQL as the database and implements CRUD operations for user management.
The code includes connection pooling configuration for optimal database performance, logging middleware to track all incoming requests, and CORS middleware to handle cross-origin requests. The API provides endpoints for retrieving all users and creating new users, with proper JSON encoding/decoding and HTTP status codes.
Expected output when running the server:
12024/01/01 12:00:00 Successfully connected to database
22024/01/01 12:00:00 Server starting on port :8080...
When making requests to the API:
- GET /api/users returns a JSON array of all users
- POST /api/users with JSON body creates a new user and returns the created user with ID and timestamp
Example 2: WebSocket Chat Application
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "net/http"
8 "sync"
9 "time"
10
11 "github.com/gorilla/websocket"
12)
13
14// Message represents a chat message
15type Message struct {
16 Username string `json:"username"`
17 Text string `json:"text"`
18 Timestamp int64 `json:"timestamp"`
19}
20
21// Client represents a connected WebSocket client
22type Client struct {
23 conn *websocket.Conn
24 send chan Message
25 name string
26}
27
28// Hub maintains the set of active clients and broadcasts messages
29type Hub struct {
30 clients map[*Client]bool
31 broadcast chan Message
32 register chan *Client
33 unregister chan *Client
34 mu sync.RWMutex
35}
36
37var upgrader = websocket.Upgrader{
38 ReadBufferSize: 1024,
39 WriteBufferSize: 1024,
40 CheckOrigin: func(r *http.Request) bool {
41 return true
42 },
43}
44
45func newHub() *Hub {
46 return &Hub{
47 clients: make(map[*Client]bool),
48 broadcast: make(chan Message),
49 register: make(chan *Client),
50 unregister: make(chan *Client),
51 }
52}
53
54func (h *Hub) run() {
55 for {
56 select {
57 case client := <-h.register:
58 h.mu.Lock()
59 h.clients[client] = true
60 h.mu.Unlock()
61 log.Printf("Client connected: %s", client.name)
62 case client := <-h.unregister:
63 h.mu.Lock()
64 if _, exists := h.clients[client]; exists {
65 delete(h.clients, client)
66 close(client.send)
67 log.Printf("Client disconnected: %s", client.name)
68 }
69 h.mu.Unlock()
70 case message := <-h.broadcast:
71 h.mu.RLock()
72 for client := range h.clients {
73 select {
74 case client.send <- message:
75 default:
76 close(client.send)
77 delete(h.clients, client)
78 }
79 }
80 h.mu.RUnlock()
81 }
82 }
83}
84
85func (c *Client) readPump() {
86 defer func() {
87 h.unregister <- c
88 c.conn.Close()
89 }()
90 c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
91 c.conn.SetPongHandler(func(string) error {
92 c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
93 return nil
94 })
95
96 for {
97 _, message, err := c.conn.ReadMessage()
98 if err != nil {
99 if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
100 log.Printf("Error reading message: %v", err)
101 }
102 break
103 }
104
105 var msg Message
106 err = json.Unmarshal(message, &msg)
107 if err != nil {
108 log.Printf("Invalid message format: %v", err)
109 continue
110 }
111 msg.Timestamp = time.Now().Unix()
112 h.broadcast <- msg
113 }
114}
115
116func (c *Client) writePump() {
117 ticker := time.NewTicker(30 * time.Second)
118 defer func() {
119 ticker.Stop()
120 c.conn.Close()
121 }()
122
123 for {
124 select {
125 case message, ok := <-c.send:
126 c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
127 if !ok {
128 c.conn.WriteMessage(websocket.CloseMessage, []byte{})
129 return
130 }
131
132 w, err := c.conn.NextWriter(websocket.TextMessage)
133 if err != nil {
134 return
135 }
136 jsonMessage, _ := json.Marshal(message)
137 w.Write(jsonMessage)
138
139 n := len(c.send)
140 for i := 0; i < n; i++ {
141 jsonMessage, _ := json.Marshal(<-c.send)
142 w.Write(jsonMessage)
143 }
144
145 if err := w.Close(); err != nil {
146 return
147 }
148 case <-ticker.C:
149 c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
150 if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
151 return
152 }
153 }
154 }
155}
156
157var h = newHub()
158
159func serveWs(w http.ResponseWriter, r *http.Request) {
160 conn, err := upgrader.Upgrade(w, r, nil)
161 if err != nil {
162 log.Println(err)
163 return
164 }
165
166 username := r.URL.Query().Get("username")
167 if username == "" {
168 username = "Anonymous"
169 }
170
171 client := &Client{
172 conn: conn,
173 send: make(chan Message, 256),
174 name: username,
175 }
176
177 h.register <- client
178 go client.writePump()
179 go client.readPump()
180}
181
182func main() {
183 go h.run()
184
185 http.HandleFunc("/ws", serveWs)
186 http.Handle("/", http.FileServer(http.Dir("./static")))
187
188 log.Println("WebSocket server starting on :8080...")
189 err := http.ListenAndServe(":8080", nil)
190 if err != nil {
191 log.Fatal("ListenAndServe: ", err)
192 }
193}
This WebSocket chat application demonstrates real-time communication capabilities in Go. The server maintains a hub that manages connected clients and broadcasts messages to all participants.
The implementation includes proper connection management with read/write pumps for each client, heartbeat mechanisms using ping/pong messages to detect dead connections, and concurrent-safe client registration/deregistration using mutexes. The hub uses channels to handle client registration, unregistration, and message broadcasting in a non-blocking manner.
The WebSocket upgrader is configured to accept connections from any origin (useful for development), and the server serves static files from a “static” directory for the chat interface. Clients connect using the /ws endpoint with an optional username parameter.
Expected behavior:
- Multiple clients can connect simultaneously
- Messages sent by any client are broadcast to all connected clients
- Connection timeouts are handled gracefully
- The server maintains a list of active clients and cleans up disconnected ones
Best Practices and Common Pitfalls
Always handle errors properly - Go’s explicit error handling can seem verbose, but it’s crucial for building robust web applications. Never ignore errors, especially when dealing with database operations, file I/O, or external API calls. Use proper HTTP status codes and meaningful error messages for API responses.
Use context for request-scoped values - The context package is essential for managing request deadlines, cancellation signals, and passing request-scoped values through your call chain. Always pass context.Context as the first parameter in functions that might block or need cancellation support.
Implement proper middleware chaining - When building middleware, ensure they call the next handler in the chain. Use third-party routers like Gorilla Mux or Chi for more sophisticated routing needs, as they provide better middleware support and route grouping capabilities than the default ServeMux.
Secure your applications - Always validate and sanitize user inputs to prevent injection attacks. Use parameterized queries for database operations, implement proper authentication and authorization, and consider using HTTPS in production. Set appropriate security headers and implement rate limiting to prevent abuse.
Avoid common pitfalls - Don’t use global variables for request-scoped data, as this can lead to race conditions in concurrent environments. Be careful with closures in goroutines that capture loop variables. Always close resources like database connections, files, and response bodies to prevent resource leaks.
Monitor and log effectively - Implement structured logging with proper log levels. Use metrics and monitoring to track application performance and health. Consider using observability tools like OpenTelemetry for distributed tracing in complex applications.
Conclusion
The “Build web application with Golang” resource is an invaluable guide for anyone looking to master web development with Go. With its comprehensive coverage of HTTP servers, routing, database integration, WebSocket communication, and security best practices, it provides everything needed to build production-ready web applications. The 44,185 stars on GitHub demonstrate its popularity and effectiveness as a learning resource.
Whether you’re building REST APIs, real-time chat applications, or full-stack web applications, the concepts and patterns covered in this guide will help you write clean, efficient, and maintainable Go code. The practical examples and real-world scenarios make it easy to apply the knowledge directly to your projects.
For more information and to access all the examples and source code, visit the GitHub repository: https://github.com/astaxie/build-web-application-with-golang