Web Service Without Dependencies

Web Service only using Go Standard Library

Go is well-known for its simplicity and robust standard library, which makes it easy to build web services without requiring external frameworks or libraries, which might result smaller binary size and less compilation time.

Setting Up a Basic HTTP Server

The core of any Go web service is the net/http package. The http.ListenAndServe function starts an HTTP server.

package main
 
import (
	"fmt"
	"net/http"
)
 
func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Welcome to the Go web service!")
	})
 
	fmt.Println("Starting server on :8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}

Handlers for Different Endpoints

You can create multiple handlers to serve different endpoints.

package main
 
import (
	"fmt"
	"net/http"
)
 
func main() {
	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/about", aboutHandler)
 
	fmt.Println("Starting server on :8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}
 
func homeHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "This is the homepage.")
}
 
func aboutHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "This is the about page.")
}

Visit http://localhost:8080 and http://localhost:8080/about to see the responses.

Parsing URL Parameters

To handle dynamic routes or query parameters, use the r.URL object.

package main
 
import (
	"fmt"
	"net/http"
)
 
func queryHandler(w http.ResponseWriter, r *http.Request) {
	name := r.PathValue("name")
 
	location := r.URL.Query().Get("location")
	if name == "" {
		name = "Guest"
	}
	fmt.Fprintf(w, "Hello, %s, from %s!", name, location)
}
 
func main() {
	http.HandleFunc("/greet/{name}", queryHandler)
 
	fmt.Println("Starting server on :8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}

Visit http://localhost:8080/greet?name=John to see a personalized greeting.

Handling JSON Data

To work with JSON data, use the encoding/json package for parsing and encoding.

package main
 
import (
	"encoding/json"
	"fmt"
	"net/http"
)
 
type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}
 
func jsonPostHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
		return
	}
 
	var user User
	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}
 
	fmt.Fprintf(w, "Received: %+v\n", user)
}
 
func main() {
	http.HandleFunc("/user", jsonPostHandler)
 
	fmt.Println("Starting server on :8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}

Middleware for Reusable Logic

Middleware is used to add common functionality like logging or authentication.

It creates a new ServerMux type, which is a server multiplexer, which matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern.

The standard ListenAndServe uses the DefaultServerMux.

package main
 
import (
	"fmt"
	"net/http"
)
 
func homeHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "This is the homepage.")
}
 
func loggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("Received request: %s %s\n", r.Method, r.URL.Path)
		next.ServeHTTP(w, r)
	})
}
 
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", homeHandler)
 
	wrappedMux := loggingMiddleware(mux)
 
	fmt.Println("Starting server on :8080")
	if err := http.ListenAndServe(":8080", wrappedMux); err != nil {
		panic(err)
	}
}
Last updated on