Go (or Golang) is a popular language for building web applications due to its simplicity, performance, and built-in support for concurrency. One of the most common tasks in web development is creating a REST API to serve data to clients. In this guide, we’ll explain how to build a REST API in Go using the Gorilla Mux router.
Gorilla Mux is a powerful HTTP request router and URL matcher for building Go web servers. It provides features like URL variables, middleware support, and routing based on HTTP methods (GET, POST, PUT, DELETE).
Let’s dive into the process of building a REST API with Go and Gorilla Mux step by step.
A REST API (Representational State Transfer) is a set of web services that allow client applications to interact with a backend server via HTTP requests. It uses standard HTTP methods like GET, POST, PUT, and DELETE to perform operations on resources.
In REST APIs:
For example, a simple URL to retrieve a list of users could be:
GET /api/users
To create a new user:
POST /api/users
To get started with Gorilla Mux in Go, you first need to install it. Gorilla Mux is available as a Go module, and you can install it using go get
.
Run the following command to install Gorilla Mux:
go get -u github.com/gorilla/mux
Once Gorilla Mux is installed, you can start using it to handle HTTP routing in your application.
Let’s start by creating a basic structure for the project. For this example, let’s assume we’re building an API to manage books in a library system.
mkdir go-book-api
cd go-book-api
go mod init go-book-api
main.go
where the main application will reside.Let’s define the basic structures needed for a book management API. We will define:
In main.go
, let’s define the Book
struct, which will represent a book in our library.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
// Book struct to hold book data
type Book struct {
ID string `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
Price float64 `json:"price"`
}
In this code:
Now, let’s define the routes that will handle the GET, POST, PUT, and DELETE operations for books. We’ll create functions for each HTTP method to handle the request.
// Global slice to store books
var books []Book
// GetBooks handler to return all books
func GetBooks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(books)
}
// CreateBook handler to add a new book
func CreateBook(w http.ResponseWriter, r *http.Request) {
var book Book
_ = json.NewDecoder(r.Body).Decode(&book)
books = append(books, book)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(book)
}
// GetBookByID handler to get a book by ID
func GetBookByID(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
for _, book := range books {
if book.ID == id {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(book)
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
// UpdateBook handler to update an existing book
func UpdateBook(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
var updatedBook Book
_ = json.NewDecoder(r.Body).Decode(&updatedBook)
for i, book := range books {
if book.ID == id {
books[i] = updatedBook
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(updatedBook)
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
// DeleteBook handler to delete a book by ID
func DeleteBook(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
for i, book := range books {
if book.ID == id {
books = append(books[:i], books[i+1:]...)
w.WriteHeader(http.StatusNoContent)
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
Here’s a breakdown of what each handler does:
Now, we need to set up the Gorilla Mux router to map the HTTP requests to their respective handlers.
func main() {
// Initialize some sample books
books = append(books, Book{ID: "1", Title: "1984", Author: "George Orwell", Price: 9.99})
books = append(books, Book{ID: "2", Title: "To Kill a Mockingbird", Author: "Harper Lee", Price: 7.99})
// Create a new router
router := mux.NewRouter()
// Define routes
router.HandleFunc("/api/books", GetBooks).Methods("GET")
router.HandleFunc("/api/books", CreateBook).Methods("POST")
router.HandleFunc("/api/books/{id}", GetBookByID).Methods("GET")
router.HandleFunc("/api/books/{id}", UpdateBook).Methods("PUT")
router.HandleFunc("/api/books/{id}", DeleteBook).Methods("DELETE")
// Start the server
log.Fatal(http.ListenAndServe(":8080", router))
}
In the main
function:
books
slice.mux.NewRouter()
.8080
using http.ListenAndServe
.We’ve already discussed how to handle different HTTP methods for our API:
These handlers are linked to specific routes, and Gorilla Mux takes care of routing the request to the correct handler based on the URL and HTTP method.
Middleware in Go is a function that wraps HTTP requests and can perform additional tasks, such as logging, authentication, and error handling. You can apply middleware to individual routes or to all routes globally.
Here’s an example of a simple logging middleware:
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Request: %s %s\n", r.Method, r.URL)
next.ServeHTTP(w, r)
})
}
func main() {
// Initialize books and router as before
// ...
// Create a new router
router := mux.NewRouter()
// Apply middleware
router.Use(LoggingMiddleware)
// Define routes
// ...
}
In this example, the LoggingMiddleware prints out the HTTP method and URL of every incoming request.
One of the most common tasks when building a REST API is working with JSON data. Go’s encoding/json
package makes it easy to encode and decode JSON.
In our handlers:
json.NewEncoder(w).Encode(data)
to send a Go struct as JSON in the response.json.NewDecoder(r.Body).Decode(&data)
to read JSON from the request body and convert it into a Go struct.Good error handling is crucial for a REST API. In the example above, we used http.Error
to return error responses when a resource is not found.
You can also return specific status codes and error messages:
http.Error(w, "Book not found", http.StatusNotFound)
For more complex APIs, you can define custom error types and use middleware to handle errors globally.
You can test the API using tools like Postman, cURL, or any HTTP client to send requests to your API.
GET all books:
curl http://localhost:8080/api/books
POST a new book:
curl -X POST -H "Content-Type: application/json" -d '{"id":"3", "title":"Go Programming", "author":"John Doe", "price": 19.99}' http://localhost:8080/api/books
PUT update a book:
curl -X PUT -H "Content-Type: application/json" -d '{"id":"1", "title":"1984", "author":"George Orwell", "price": 8.99}' http://localhost:8080/api/books/1
DELETE a book:
curl -X DELETE http://localhost:8080/api/books/1
To run your Go API, use the following command in your terminal:
go run main.go
The server will start running on http://localhost:8080
. You can then test your API using the methods mentioned above.
In this tutorial, we’ve built a simple REST API in Go using Gorilla Mux. We’ve covered:
This forms a solid foundation for building more advanced APIs with Go. You can extend this API by adding more features, such as user authentication, database integration, and complex business logic.