In this article, we will look in-depth at the process of creating an API that will let users both create and view events. If you’re interested in exploring further, you can find the final source code for this tutorial on GitHub.
Steps to take before getting started
- To work through the tutorial, you need to have Go already installed. If you haven’t yet, please visit here to install the application.
- Once Go is installed on your computer, open your GOPATH (this is the name given to the directory where each of your projects will be located). The GOPATH will already be configured upon installation of Go. You should see three folders bin,pkg and src in your GOPATH. In the src environment make a new folder and name it GitHub.com, then place your GitHub credentials as a separate folder so that you can use it to hold all of your Go projects.
- You should now be in this directory $ GOPATH/src/github.com/<Github username>. This is the location of your current working directory, and the place where we will build the new API.
Let’s go
The first step is to make a new folder and name it go-rest-api, then you should change the directory into this new folder.
Then we need to create a main.go file, and make it the entry point.
Within your favorite text editor, please open the main.go file so we can start building.
The next step is to define the package as main and use that primary function to test out the app.
package mainimport "fmt"func main() { fmt.Println("Hello World!")}
view rawmain.go hosted with ❤ by GitHub
Let’s go ahead and test up to this point.
Note: Remember, this will be the general procedure for building and running our API.
Let’s take a look back so that we can better understand what we just did.
There are reusable snippets of code called packages to be used as shared libraries. When coding executable programs, we can use the package “main.” Using this package lets the Go compiler know that the goal is an executable program and not a shared library.
The function name func main, when combined with package main, denotes the application point of entry.
The Golang package fmt implements formatted I/O.
Use Gorilla Mux to configure the HTTP Server
Gorilla Mux is a specific package used to implement a request router and a dispatcher for pairing incoming requests with the correct handler.
You should install Gorilla Mux, but make sure you do so while still operating in your GOPATH:
Then, we should make the first home endpoint “/” using Gorilla Mux.
package mainimport ( "fmt" "log" "net/http" "github.com/gorilla/mux")func homeLink(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Welcome home!")}func main() { router := mux.NewRouter().StrictSlash(true) router.HandleFunc("/", homeLink) log.Fatal(http.ListenAndServe(":8080", router))}
view rawmain.go hosted with ❤ by GitHub
For this tutorial, when the “/” endpoint is hit, the function will return the “Welcome home!” string.
The server that we created will run on http://localhost:8080.
Here is what you should see in Postman:
Make a dummy database
Next, in the process of creating your API, inside the main.go file, let’s make a struct and slice.
type event struct { ID string `json:"ID"` Title string `json:"Title"` Description string `json:"Description"`}type allEvents []eventvar events = allEvents{ { ID: "1", Title: "Introduction to Golang", Description: "Come join us for a chance to learn how golang works and get to eventually try it out", },}
view rawmain.go hosted with ❤ by GitHub
For our event struct, we will only be using the Title and Description fields. The slice, or dummy database, should only hold event structs. Therefore, from the slice, we can call a new event struct, read what it says, change it if necessary, or delete it.
Creating an event
func createEvent(w http.ResponseWriter, r *http.Request) { var newEvent event reqBody, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Fprintf(w, "Kindly enter data with the event title and description only in order to update") } json.Unmarshal(reqBody, &newEvent) events = append(events, newEvent) w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(newEvent)}
view rawmain.go hosted with ❤ by GitHub
The data for creating a new event comes from the user’s end when the user inputs data in the form of an http request data. When input, the request data is not in a form that is readable by humans, so to translate it into a slice, we use the package ioutil.
After it has been translated into a slice, we fit it into an event struct by unmarshalling it. Once the slice is successfully created, we can append the event struct into the new events slice and show the new event with an http response of 201 Created Status Code.
Now this is what the current iteration of your API should look like in Postman:
Get an event
func getOneEvent(w http.ResponseWriter, r *http.Request) { eventID := mux.Vars(r)["id"] for _, singleEvent := range events { if singleEvent.ID == eventID { json.NewEncoder(w).Encode(singleEvent) } }}
view rawmain.go hosted with ❤ by GitHub
By using the GET Method, we will be able to access the endpoint for getting one event and it will look like this
/events/{id}. Within Gorilla Mux, we can obtain the value to be inserted into the “id” to filter out a selected event from the events slice. Once an “id” that resembles the input “id” is located, its value is obtained from the events slice and displayed as a response to the user within the API.
This is how that looks in Postman:
Gathering all events
func getAllEvents(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(events)}
view rawmain.go hosted with ❤ by GitHub
In order to gather all events in the slice, you simply need to display the entire events slice.
Another view in Postman:
Update an event
func updateEvent(w http.ResponseWriter, r *http.Request) { eventID := mux.Vars(r)["id"] var updatedEvent event reqBody, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Fprintf(w, "Kindly enter data with the event title and description only in order to update") } json.Unmarshal(reqBody, &updatedEvent) for i, singleEvent := range events { if singleEvent.ID == eventID { singleEvent.Title = updatedEvent.Title singleEvent.Description = updatedEvent.Description events = append(events[:i], singleEvent) json.NewEncoder(w).Encode(singleEvent) } }}
view rawmain.go hosted with ❤ by GitHub
We use a PATCH Method and an endpoint of /events/{id} to update an existing event. Again, with the help of Gorilla Mux, we find the value of the “id” and input it into the endpoint to call a specific event slice. Once the corresponding “id” has been located, we can change the values of the Title and Description fields within the event struct.
Then, we change the value of the struct in the events slice. Next, we return the updated value for the event struct to the user as a response.
Here’s is what that looks like within Postman:
Remove an event
func deleteEvent(w http.ResponseWriter, r *http.Request) { eventID := mux.Vars(r)["id"] for i, singleEvent := range events { if singleEvent.ID == eventID { events = append(events[:i], events[i+1:]...) fmt.Fprintf(w, "The event with ID %v has been deleted successfully", eventID) } }}
view rawmain.go hosted with ❤ by GitHub
To remove an event from the API, we need to use the DELETE Method and the same endpoint, which is /events/{id}. Again, we will use Gorilla Mux to obtain the value of the “id” and use that information to filter for the requested event in the events slice. Once the correct “id” is located, we can delete the occurrence in the slice and provide the user with a successful deletion message.
The new view in Postman:
All routes
We should combine all of the routes used in this tutorial into a completed API…
func main() { initEvents() router := mux.NewRouter().StrictSlash(true) router.HandleFunc("/", homeLink) router.HandleFunc("/event", createEvent).Methods("POST") router.HandleFunc("/events", getAllEvents).Methods("GET") router.HandleFunc("/events/{id}", getOneEvent).Methods("GET") router.HandleFunc("/events/{id}", updateEvent).Methods("PATCH") router.HandleFunc("/events/{id}", deleteEvent).Methods("DELETE") log.Fatal(http.ListenAndServe(":8080", router))}
view rawmain.go hosted with ❤ by GitHub
The end
I have provided you with the entire main.go file used in this tutorial. Please go check it out, and happy coding!
package mainimport ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "github.com/gorilla/mux")type event struct { ID string `json:"ID"` Title string `json:"Title"` Description string `json:"Description"`}type allEvents []eventvar events = allEvents{ { ID: "1", Title: "Introduction to Golang", Description: "Come join us for a chance to learn how golang works and get to eventually try it out", },}func homeLink(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Welcome home!")}func createEvent(w http.ResponseWriter, r *http.Request) { var newEvent event reqBody, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Fprintf(w, "Kindly enter data with the event title and description only in order to update") } json.Unmarshal(reqBody, &newEvent) events = append(events, newEvent) w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(newEvent)}func getOneEvent(w http.ResponseWriter, r *http.Request) { eventID := mux.Vars(r)["id"] for _, singleEvent := range events { if singleEvent.ID == eventID { json.NewEncoder(w).Encode(singleEvent) } }}func getAllEvents(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(events)}func updateEvent(w http.ResponseWriter, r *http.Request) { eventID := mux.Vars(r)["id"] var updatedEvent event reqBody, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Fprintf(w, "Kindly enter data with the event title and description only in order to update") } json.Unmarshal(reqBody, &updatedEvent) for i, singleEvent := range events { if singleEvent.ID == eventID { singleEvent.Title = updatedEvent.Title singleEvent.Description = updatedEvent.Description events = append(events[:i], singleEvent) json.NewEncoder(w).Encode(singleEvent) } }}func deleteEvent(w http.ResponseWriter, r *http.Request) { eventID := mux.Vars(r)["id"] for i, singleEvent := range events { if singleEvent.ID == eventID { events = append(events[:i], events[i+1:]...) fmt.Fprintf(w, "The event with ID %v has been deleted successfully", eventID) } }}func main() { initEvents() router := mux.NewRouter().StrictSlash(true) router.HandleFunc("/", homeLink) router.HandleFunc("/event", createEvent).Methods("POST") router.HandleFunc("/events", getAllEvents).Methods("GET") router.HandleFunc("/events/{id}", getOneEvent).Methods("GET") router.HandleFunc("/events/{id}", updateEvent).Methods("PATCH") router.HandleFunc("/events/{id}", deleteEvent).Methods("DELETE") log.Fatal(http.ListenAndServe(":8080", router))}
view rawmain.go hosted with ❤ by GitHub