Create a REST API with Go
Tags: go, rest, api, beginners
Published: 2022-10-10 18:06:35

In this [example](https://github.com/janirefdez/ArticleRestApi) we will create a simple REST API with Go. 
- Get all articles.
- Get specific article.
- Add new article.
- Update Article.
- Delete article.

Let's get started!

## Install Go

Make sure that Go language is installed in your system. Open a new terminal and run the following command.

```
go version
```

It should print the Go language version that you have. 
Otherwise install the Go language before proceeding.

```
brew install go
```

## Create a new Go project

First, create a new directory for the Go project and go into the directory created.

```
mkdir articleRestApi

cd articleRestApi
```

Now we need to create the project module with the `go mod init <path>` command. 
The module path normally is the URL where you publish your project. In my case it will be `github.com/janirefdez/ArticleRestApi`

```
go mod init github.com/janirefdez/ArticleRestApi
```

This command will create a file called `go.mod`. It will contain information about the Go module path and the Go language version. When adding more dependencies to the project, they will be mentioned in this file.


## Set up 

In this example we are going to use a [gorilla/mux](https://github.com/gorilla/mux) router instead of the traditional net/http router.

And we are also going to use [google/uuid](https://github.com/google/uuid) to generate random uuids.

Run the following command to be able to use `gorilla/mux`

```
go get github.com/gorilla/mux
```

And for `google/uuid` run
```
go get github.com/google/uuid
```

You should see that in `go.mod` it should appear the dependencies `gorilla/mux` and `google/uuid`. And a new file `go.sum` should have been created. 


## Create the base for our API

Now let's create the `main.go` file.

```
touch main.go
```

and add the following code to it.

```
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

func homePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Welcome to the Article REST API!")
	fmt.Println("Article REST API")
}

func handleRequests() {
	// create a new instance of a mux router
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	log.Fatal(http.ListenAndServe(":8080", myRouter))
}

func main() {
	handleRequests()
}

```

This has:
- A homePage function that will handle all requests to our root URL.
- A handleRequests function that will match the URL path hit with a defined function
- A main function which will kick off our REST API.

Let's run our API to see what happens ;)!

```
go run main.go
```

If you now check `http://localhost:8080/` you should see message `Welcome to the Article REST API!`

![Welcome to the Article REST API!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cf30dwqxx35ep201d2u0.png)

This means we have now successfully created the base for our REST API. 

But first we need to organize the project because we don't want to code everything on `main.go`. So let's move `main.go` to a new directory called `cmd`. At this point the project structure should look like this.

```
├── cmd
│   └── main.go
├── go.sum
└── go.mod
```

## Create Article Model and Mock

Now we need to define our Article model. In this example, Article will contain an `Id`, a `Title`, a `Description` and `Content`. 

Let's create file `pkg/models/article.go`.

```
package models

type Article struct {
	Id      string `json:"Id"`
	Title   string `json:"Title"`
	Desc    string `json:"desc"`
	Content string `json:"content"`
}
```
and also create mocks for that Article model `pkg/mocks/article.go`.

```
package mocks

import "github.com/janirefdez/ArticleRestApi/pkg/models"

var Articles = []models.Article{
	{Id: "8617bf49-39a9-4268-b113-7b6bcd189ba2", Title: "Article 1", Desc: "Article Description 1", Content: "Article Content 1"},
	{Id: "38da7ce2-02b5-471a-90b8-c299f2ef132e", Title: "Article 2", Desc: "Article Description 2", Content: "Article Content 2"},
}
```
Now our project looks like this:

```
├── cmd
│   └── main.go
├── pkg
│    ├── mocks
│    │   └── article.go
│    └── models
│        └── article.go
├── go.sum
└── go.mod
```

## API EndPoints

Now we are going to start creating our API endpoints. 

**Get All Articles**

This will be an `HTTP GET` request that will return all the articles.

Create a new handler `pkg/handlers/GetAllArticles.go`.

```
package handlers

import (
	"encoding/json"
	"net/http"

	"github.com/janirefdez/ArticleRestApi/pkg/mocks"
)

func GetAllArticles(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(mocks.Articles)
}
```

The call to `json.NewEncoder(w).Encode(mocks.Articles)` does the job of encoding our articles array into a JSON string and then writing as part of our response.

In function `handleRequests()` from `cmd/main.go` include the new endpoint.  

```
func handleRequests() {
	// create a new instance of a mux router
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	myRouter.HandleFunc("/articles", handlers.GetAllArticles).Methods(http.MethodGet)
	log.Fatal(http.ListenAndServe(":8080", myRouter))
}
```

If we now run `main.go` we can do the request to get all the articles. 

```
go run cmd/main.go
```

*Note: It will return the articles defined in `pkg/mocks/article.go`.

![GetArticles Paw](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w0sgbvf3des1yjr8eb43.png)

**Get Article By Id**

This will be an `HTTP GET` request that will return one article.

Create a new handler `pkg/handlers/GetArticle.go`.

```
package handlers

import (
	"encoding/json"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/janirefdez/ArticleRestApi/pkg/mocks"
)

func GetArticle(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	for _, article := range mocks.Articles {
		if article.Id == id {
			w.Header().Add("Content-Type", "application/json")
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(article)
			break
		}
	}
}
```

And in function `handleRequests()` from `cmd/main.go` include the new endpoint.  

```
func handleRequests() {
	// create a new instance of a mux router
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	myRouter.HandleFunc("/articles", handlers.GetAllArticles).Methods(http.MethodGet)
	myRouter.HandleFunc("/articles/{id}", handlers.GetArticle).Methods(http.MethodGet)
	log.Fatal(http.ListenAndServe(":8080", myRouter))
}
```

If we now run `main.go` we can do the request to get an specific article by id.

```
go run cmd/main.go
```

![GetArticle Paw](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vs8v5x08yzg693p28ca.png)

**Create new Article**
This will be an `HTTP POST` request that will add a new article.

Create a new handler `pkg/handlers/AddArticle.go`.
```
package handlers

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/janirefdez/ArticleRestApi/pkg/mocks"
	"github.com/janirefdez/ArticleRestApi/pkg/models"

	"github.com/google/uuid"
)

func AddArticle(w http.ResponseWriter, r *http.Request) {
	// Read to request body
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		log.Fatalln(err)
	}
	var article models.Article
	json.Unmarshal(body, &article)

	article.Id = (uuid.New()).String()
	mocks.Articles = append(mocks.Articles, article)

	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode("Created")
}
```

In this function we are reading the information of the article the client has sent. And we are generating a random uuid to that article. 

If everything goes well, we send response HTTP 201. 

*Note: 201 -> created success status response code indicates that the request has succeeded and has led to the creation of a resource.

And in function `handleRequests()` from `cmd/main.go` include the new endpoint.  

```
func handleRequests() {
	// create a new instance of a mux router
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	myRouter.HandleFunc("/articles", handlers.GetAllArticles).Methods(http.MethodGet)
	myRouter.HandleFunc("/articles/{id}", handlers.GetArticle).Methods(http.MethodGet)
	myRouter.HandleFunc("/articles", handlers.AddArticle).Methods(http.MethodPost)
	log.Fatal(http.ListenAndServe(":8080", myRouter))
}

```

If we now run `main.go` we can do the request to add a new article.

![Add Article Paw](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xgwtmul4ogzjguvq5nwq.png)

If you then do the request to get all articles, the new article you have added should be there. 

**Update Article by id**
This will be an `HTTP PUT` request that will update an article.

Create a new handler `pkg/handlers/UpdateArticle.go`.

```
package handlers

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/janirefdez/ArticleRestApi/pkg/mocks"
	"github.com/janirefdez/ArticleRestApi/pkg/models"
)

func UpdateArticle(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	// Read request body
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		log.Fatalln(err)
	}

	var updatedArticle models.Article
	json.Unmarshal(body, &updatedArticle)

	for index, article := range mocks.Articles {
		if article.Id == id {
			article.Title = updatedArticle.Title
			article.Desc = updatedArticle.Desc
			article.Content = updatedArticle.Content

			mocks.Articles[index] = article

			w.Header().Add("Content-Type", "application/json")
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode("Updated")
			break
		}
	}
}
```
And in function `handleRequests()` from `cmd/main.go` include the new endpoint.  

```
func handleRequests() {
	// create a new instance of a mux router
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	myRouter.HandleFunc("/articles", handlers.GetAllArticles).Methods(http.MethodGet)
	myRouter.HandleFunc("/articles/{id}", handlers.GetArticle).Methods(http.MethodGet)
	myRouter.HandleFunc("/articles", handlers.AddArticle).Methods(http.MethodPost)
	myRouter.HandleFunc("/articles/{id}", handlers.UpdateArticle).Methods(http.MethodPut)
	log.Fatal(http.ListenAndServe(":8080", myRouter))
}
```

If we now run `main.go` we can do the request to update an article.

![Update Article Paw](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vjj4sol4c1p9z7fjfe8v.png)

If you then do the request to get all articles, the article should be updated.

 **Delete Article by id**

This will be an `HTTP DELETE` request that will delete an article.

Create a new handler `pkg/handlers/DeleteArticle.go`.

```
package handlers

import (
	"encoding/json"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/janirefdez/ArticleRestApi/pkg/mocks"
)

func DeleteArticle(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	for index, article := range mocks.Articles {
		if article.Id == id {
			mocks.Articles = append(mocks.Articles[:index], mocks.Articles[index+1:]...)

			w.Header().Add("Content-Type", "application/json")
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode("Deleted")
			break
		}
	}
}
```

And in function `handleRequests()` from `cmd/main.go` include the new endpoint. 

```
func handleRequests() {
	// create a new instance of a mux router
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	myRouter.HandleFunc("/articles", handlers.GetAllArticles).Methods(http.MethodGet)
	myRouter.HandleFunc("/articles/{id}", handlers.GetArticle).Methods(http.MethodGet)
	myRouter.HandleFunc("/articles", handlers.AddArticle).Methods(http.MethodPost)
	myRouter.HandleFunc("/articles/{id}", handlers.UpdateArticle).Methods(http.MethodPut)
	myRouter.HandleFunc("/articles/{id}", handlers.DeleteArticle).Methods(http.MethodDelete)
	log.Fatal(http.ListenAndServe(":8080", myRouter))
}
```
If we now run `main.go` we can do the request to delete an article.

![DeleteArticle Paw](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfz3xwga78kp97jusmef.png)

If you then do the request to get all articles, the article you deleted shouldn't appear.

Now our project looks like this:

```
├── cmd
│   └── main.go
├── pkg
│    ├── handlers
│    │   ├── AddArticle.go
│    │   ├── DeleteArticle.go
│    │   ├── GetAllArticles.go
│    │   ├── GetArticle.go
│    │   └── UpdateArticle.go
│    ├── mocks
│    │   └── article.go
│    └── models
│        └── article.go
├── go.sum
└── go.mod
```

## Improvements

- Include error response when something goes wrong.
- Include error response when an article is not found.
- [Connect the API to a real database](https://dev.to/janirefdez/connect-rest-api-to-database-with-go-d8m)
- ...

But this is a simple example to learn how to create a REST API. Hope you enjoyed!!

If you want to check the whole project here you have the link: [ArticleRestApi](https://github.com/janirefdez/ArticleRestApi) 

In the next example we will [connect this API to PostgreSQL database](https://dev.to/janirefdez/connect-rest-api-to-database-with-go-d8m).

Don't forget to like and share! Thank you! :)