Web Development Fundamentals
Why net/http Matters
The Problem: Rolling your own HTTP server in Go is tempting but you'll re-invent middleware, routing, and validation badly.
The Solution: net/http is solid by itself; chi or echo add ergonomic routing without dragging in heavy frameworks; encoding/json handles serialization out of the box.
Real Impact: Go is the standard for high-throughput APIs because the language, runtime, and HTTP stack are designed together for performance.
Real-World Analogy
Think of net/http as a hotel front desk:
- Handler = a clerk who serves one type of request
- Router = the lobby concierge directing guests to the right clerk
- Middleware = the security check every guest passes through first
- Context = the guest's keycard — used by every clerk they meet
- ResponseWriter = the printer that produces the reply on the way out
Go Web Development Ecosystem
Go provides excellent built-in support for web development with the net/http package, along with a rich ecosystem of frameworks and libraries for building scalable web applications and APIs.
HTTP Foundation
Go's net/http package provides a robust foundation for web applications with built-in support for HTTP/2, TLS, and efficient request handling.
Built-in HTTP/2Framework Ecosystem
Rich ecosystem of web frameworks offering different trade-offs between performance, features, and ease of use for various application needs.
Gin Echo FiberPerformance & Scalability
Go's concurrency model and efficient runtime make it ideal for building high-performance web services that can handle thousands of concurrent requests.
Concurrent ScalableBasic HTTP Server
Create a web server using Go's net/http package.
package main
import (
"fmt"
"html/template"
"log"
"net/http"
)
func main() {
// Handle routes
http.HandleFunc("/", homeHandler)
http.HandleFunc("/about", aboutHandler)
http.HandleFunc("/api/users", usersAPIHandler)
// Serve static files
fs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
log.Println("Server starting on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
fmt.Fprintf(w, "Welcome to Go Web Server
")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
data := struct {
Title string
Message string
}{
Title: "About Us",
Message: "Built with Go",
}
tmpl := template.Must(template.New("about").Parse(`
{{.Title}}
{{.Message}}
`))
tmpl.Execute(w, data)
}
func usersAPIHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case http.MethodGet:
fmt.Fprint(w, `{"users": [{"id": 1, "name": "John"}]}`)
case http.MethodPost:
// Handle POST request
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
Gin Web Framework
Build robust web applications using the Gin framework.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Todo struct {
ID string `json:"id"`
Title string `json:"title" binding:"required"`
Completed bool `json:"completed"`
}
var todos = []Todo{
{ID: "1", Title: "Learn Go", Completed: false},
{ID: "2", Title: "Build API", Completed: false},
}
func main() {
router := gin.Default()
// Middleware
router.Use(gin.Logger())
router.Use(gin.Recovery())
// Static files
router.Static("/assets", "./assets")
router.LoadHTMLGlob("templates/*")
// HTML rendering
router.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "Home Page",
"todos": todos,
})
})
// API routes
api := router.Group("/api")
{
api.GET("/todos", getTodos)
api.GET("/todos/:id", getTodoByID)
api.POST("/todos", createTodo)
api.PUT("/todos/:id", updateTodo)
api.DELETE("/todos/:id", deleteTodo)
}
router.Run(":8080")
}
func getTodos(c *gin.Context) {
c.JSON(http.StatusOK, todos)
}
func getTodoByID(c *gin.Context) {
id := c.Param("id")
for _, todo := range todos {
if todo.ID == id {
c.JSON(http.StatusOK, todo)
return
}
}
c.JSON(http.StatusNotFound, gin.H{"error": "Todo not found"})
}
func createTodo(c *gin.Context) {
var newTodo Todo
if err := c.ShouldBindJSON(&newTodo); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
todos = append(todos, newTodo)
c.JSON(http.StatusCreated, newTodo)
}
Template Rendering
Render dynamic HTML templates with Go's template engine.
// templates/layout.html
{{define "layout"}}
{{.Title}}
{{template "content" .}}