Using Golang to Create a RESTful JSON API

using golang to create a resftul json api

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.

RESTful JSON API code

Then we need to create a main.go file, and make it the entry point.

RESTful JSON API code

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.

api code
Here’s what you should see as the result of our test:
api code

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:

RESTful JSON API code

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:

postman code

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:

postman code

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:

postman code

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:

postman code

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:

postman code

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:

postman code

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

Are you a Go developer interested in growing your software engineering career? Apply to join the Andela Talent Network today.

Related posts

The latest articles from Andela.

Visit our blog

Writer's Room: 5 tips for technical writing

Are you a technologist craving to share your work with the world? Then turn your technical project into a blog with our 5 top tips for technical writing.

Asynchronous and synchronous communication in the remote workplace

While synchronous collaboration was the preferred method for many global organizations, remote work has increased the popularity of asynchronous communication. But which is more beneficial, both to employees, and to business?

Navigating the tech landscape: Andela’s predictions for 2024

What will the tech industry look like in 2024? Read our predictions on how work structures, African tech talent, human-GenAI collaboration, and diversity and inclusion will shape the future of technology and your team.

We have a 96%+
talent match success rate.

The Andela Talent Operating Platform provides transparency to talent profiles and assessment before hiring. AI-driven algorithms match the right talent for the job.