In modern software development, Application Programming Interfaces (APIs) are essential for building scalable and flexible systems. APIs enable applications to communicate and exchange data, amongst other actions.
Representational State Transfer (REST) is an API specification for building simple, scalable, client-server APIs based on HTTP, stateless, cacheable, and layered.
RESTful APIs provide a medium for interaction between software components, easing the building process of distributed applications. However, building RESTful APIs can be daunting, especially for beginners; that’s where the Gorilla Mux package comes in handy.
The Gorilla Mux package is a powerful HTTP router and dispatcher that simplifies building RESTful APIs. Gorilla Mux allows you to define routes for different HTTP methods (GET
, POST
, PUT
, DELETE
, etc.) and handle requests and responses in a structured manner. The Gorilla Mux package is easy to use, and the package supports a range of middleware that you can use for logging and authentication operations, among others.
Getting Started Building Applications in Go
Getting started with building REST APIs with Go is quite easy. First, you must install the latest Go version on your system. The installation process is straightforward. Simply visit the Go downloads webpage to install the Go binary distribution for your computer’s operating system.
Run this command on your terminal to verify that you’ve successfully installed Go.
1 |
go version |
Once you have installed Go, the next step is creating a new directory and initializing the directory as a Go project.
Run these commands on your terminal to create and initialize a new Go project on your computer.
1 2 |
mkdir HumanAPI && cd HumanAPI go mod init HumanAPI |
The go mod
command initializes a new Go project in the specified working directory. The command also creates a go.mod file in the directory for managing your project’s dependencies.
Finally, you’ll need some form of persistence for your database. In this tutorial, you’ll learn how to use GORM and a SQL database for persistence for your API. GORM supports a variety of SQL databases, including MSSQL, MySQL, and PostgreSQL. Install your preferred database management system, and you’re good to go.
Building APIs with Gorilla Mux
The Gorilla Mux package is a third-party package, and you’ll need to install the package as one of your project’s dependencies to get started.
Run this command in the terminal of your project’s working directory to install the Gorilla Mux package.
1 |
go get github.com/gorilla/mux |
Here’s how to import the Gorilla Mux package into your Go files.
1 |
import "github.com/gorilla/mux" |
You’ll need to create a new router instance to work with handler functions with the Gorilla Mux package. You can use the NewRouter
function to create a new router instance.
1 |
router := mux.NewRouter() |
After creating a router instance, you can use the HandleFunc
function to map handler functions to routes and specify the HTTP method for the routes.
1 2 3 4 5 6 7 8 9 10 11 |
func main() { router := mux.NewRouter() router.HandleFunc("/humans/{id}", getHuman).Methods("GET") router.HandleFunc("/humans", createHuman).Methods("POST") router.HandleFunc("/humans/{id}", updateHuman).Methods("PUT") router.HandleFunc("/humans/{id}", deleteHuman).Methods("DELETE") log.Fatal(http.ListenAndServe(":8080", router)) } |
The main function above creates a new router instance and mounts routes to GET
, POST
, PUT
, and DELETE
requests before starting a server on port :8080
with the ListenAndServe
function of the http
package.
Setting Persistence Up GORM for the API
Most of the time, you’ll need persistence when building APIs for data storage and retrieval. GORM is the most popular ORM in the Go ecosystem, and the package is useful for working with many databases.
Run this command to install the GORM package and the SQLite database driver for the package.
1 2 |
go get gorm.io/gorm go get gorm.io/driver/sqlite //SQLite |
You can learn more about GORM from this article if you’re unfamiliar with the package. The article covers the most popular operations with the package while considering other databases.
Here’s the list of imports you’ll need to set up the API, including Gorilla Mux, GORM, and Go built-in packages.
1 2 3 4 5 6 7 8 9 |
import( "encoding/json" "fmt" "log" "net/http" "github.com/gorilla/mux" "gorm.io/driver/sqlite" "gorm.io/gorm" ) |
You’ll need a struct model that defines the database schema for queries and data migration. Here’s an example struct for the database schema.
1 2 3 4 5 |
type Human struct { ID uint `gorm:"primaryKey"` Age int `json:"age"` Name string `json:"name"` } |
The ID
field is the primary key, and the Age
and Name
fields are regular fields.
You’ll need to access your database instance from multiple handler functions, so you can resort to making it a global variable for accessibility. The DB
struct defines a GORM database instance.
1 2 3 4 |
var ( db *gorm.DB err error ) |
You can use a function to manage database auto migrations. You’ll use the Open
function of the gorm
package to open a database connection from the driver. The sqlite
package’s Open
function returns a connection instance that GORM can access and return a database instance.
After creating a database instance, you can use the AutoMigrate
function of the database instance that takes in the struct model and auto-migrates struct instances.
1 2 3 4 5 6 7 8 9 10 11 |
func DBConnection() error { db, err = gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) if err != nil { return err } err = db.AutoMigrate(&Human{}) if err != nil { return err } return nil } |
The DBConnection
function is the auto migration function. The function returns an error if there’s any during the migration process.
You can proceed to call the DBConnection
function in the main
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
func main() { err := DBConnection() if err != nil { log.Fatal("Database connection error", err) } router := mux.NewRouter() router.HandleFunc("/humans/{id}", getHuman).Methods("GET") router.HandleFunc("/humans", createHuman).Methods("POST") router.HandleFunc("/humans/{id}", updateHuman).Methods("PUT") router.HandleFunc("/humans/{id}", deleteHuman).Methods("DELETE") log.Fatal(http.ListenAndServe(":8080", router)) } |
The next step is setting up the handler functions for successful operations.
The POST Request Handler Function
Your POST request handler function would receive a JSON request body from the client, decode the JSON into the Human
struct instance and migrate the struct to the database using the Create
function of the database instance.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func createHuman(writer http.ResponseWriter, request *http.Request) { var human Human err = json.NewDecoder(request.Body).Decode(&human) if err != nil { log.Println(err) } db.Create(&human) err := json.NewEncoder(writer).Encode(human) if err != nil { log.Println(err) } } |
The createHuman
handler function creates a handler function instance with HTTP response writer and requests instances as arguments, decodes the JSON to struct with the Decode
function of the NewDecoder
function, migrates the data to the database, and returns an encoded JSON struct to the client.
Here’s a CURL request that makes a POST request to the server for the /humans
route.
1 2 |
curl -X POST -H "Content-Type: application/json" -d '{"name":"John Doe","age":30}' <http://localhost:8080/humans> |
The GET Request Handler Function
Your GET
request handler function will receive an ID
from the client’s request, search through the database for a row with the ID
and return the row’s data from the database as JSON.
1 2 3 4 5 6 7 8 9 10 |
func getHuman(writer http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) id := vars["id"] var human Human db.First(&human, id) err := json.NewEncoder(writer).Encode(human) if err != nil { log.Println(err) } } |
The getHuman
handler function accesses the data from the request using the Vars
function of the mux
package and retrieves the ID.
The human
variable is an instance of the Human
struct created to hold the row’s data with the ID, and the Encode
function of the NewEncoder
function of the JSON package encodes the human
struct instance to the client as a response.
Here’s the CURL request that makes a GET
to the server requesting data 1 as the ID.
1 |
curl <http://localhost:8080/humans/1> |
Here’s the result of the request. The request returns the data inserted into the database with the POST
request.
The PUT Request Handler Function
The PUT
request handler function would retrieve an ID from the request URL
and a JSON payload from the request body and update the ID
row with the JSON from the request body.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func updateHuman(writer http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) id := vars["id"] var human Human db.First(&human, id) err := json.NewDecoder(request.Body).Decode(&human) if err != nil { log.Println(err) } db.Save(&human) err = json.NewEncoder(writer).Encode(human) if err != nil { log.Println(err) } } |
The updateHuman handler function extracts the ID from the request just like the GET request and retrieves the data with the ID from the database. If the retrieval is successful, the data exists. The Save function of the database instance updates the database with the JSON from the body of the request and returns the JSON to the client after successfully updating the database.
The CURL request for the PUT request holds the payload with different name and age fields from the current data in the database with the ID of 1.
1 2 |
curl -X PUT -H "Content-Type: application/json" -d '{"name":"Jane Doe","age":35}' <http://localhost:8080/humans/1> |
On sending the CURL request, here’s the result of the update operation.
The DELETE Request Handler Function
Your DELETE
request handler function would retrieve an ID
from the request and delete the row with the ID
from the database before responding to the client to signal a successful delete operation.
1 2 3 4 5 6 7 8 9 10 11 |
func deleteHuman(writer http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) id := vars["id"] db.Delete(&Human{}, id) _, err := fmt.Fprintf(writer, "Human with ID = %s has been deleted", id) if err != nil { log.Println(err) } } |
The deleteHuman
function retrieves the ID from the client’s request URL and deletes the row using the Delete
function of the database instance.
After a successful deletion operation, the function writes a string with the ID
to the client.
Here’s the CURL
request for the DELETE
request. The client requests to delete the row with ID 1.
1 |
curl -X DELETE <http://localhost:8080/humans/1> |
On sending the CURL request, here’s the result of the update operation.
Conclusion
You’ve learned about RESTful APIs, the Gorilla Mux package, how to use GORM for persistence when building APIs, how to create router instances, mount routes to handler functions, and how to use the Gorilla Mux package to interact with requests.
Using GORM and Gorilla Mux can help you build scalable, maintainable APIs that handle many HTTP requests efficiently.
Load comments