This content originally appeared on DEV Community and was authored by Karan Pratap Singh
In this article, we will build a simple REST CRUD API with Go.
I've also created a video if you will like to follow along!
Setup
Let's initialize a simple Go project and to keep things simple we won't be connecting to any database in this article.
All the code from this article is available here
$ go mod init github.com/karanpratapsingh/tutorials/go/crud
Let's create our main.go
$ touch main.go
I will also install Mux which will help us with routing.
gorilla/mux
https://www.gorillatoolkit.org/pkg/mux
Package gorilla/mux
implements a request router and dispatcher for matching incoming requests to
their respective handler.
The name mux stands for "HTTP request multiplexer". Like the standard http.ServeMux
, mux.Router
matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are:
- It implements the
http.Handler
interface so it is compatible with the standardhttp.ServeMux
. - Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
- URL hosts, paths and query values can have variables with an optional regular expression.
- Registered URLs can be built, or "reversed", which helps maintaining references to resources.
- Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions…
Hello world
package main
import (
"log"
"net/http"
"encoding/json"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/books", func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode("Hello World")
})
log.Println("API is running!")
http.ListenAndServe(":4000", router)
}
Now let's run our app!
$ go run main.go
Organize
Before proceeding further, let's organize our code because we don't want to write all the code in main.go
. We will create the following project structure
├── cmd
│ └── main.go
├── pkg
│ ├── handlers
│ │ ├── AddBook.go
│ │ ├── DeleteBook.go
│ │ ├── GetAllBooks.go
│ │ ├── GetBook.go
│ │ └── UpdateBook.go
│ ├── mocks
│ │ └── book.go
│ └── models
│ └── book.go
├── go.sum
└── go.mod
Note: This is just a sample structure, feel free to create our own project structure if you like!
Cmd
Let's move our main.go
to cmd
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/karanpratapsingh/tutorials/go/crud/pkg/handlers"
)
func main() {
router := mux.NewRouter()
// Here we'll define our api endpoints
log.Println("API is running!")
http.ListenAndServe(":4000", router)
}
Models
Let's define our Book model at pkg/models/book.go
package models
type Book struct {
Id int `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
Desc string `json:"desc"`
}
Mocks
Let's create our mocks pkg/mocks/book.go
package mocks
import "github.com/karanpratapsingh/tutorials/go/crud/pkg/models"
var Books = []models.Book{
{
Id: 1,
Title: "Golang",
Author: "Gopher",
Desc: "A book for Go",
},
}
Handlers
Now, let's start defining our handlers!
Get all Books
Let's add our endpoint to cmd/main.go
router.HandleFunc("/books", handlers.GetAllBooks).Methods(http.MethodGet)
Create a new handler pkg/handlers/GetBooks.go
In this handler, we'll simply return all our mock books.
package handlers
import (
"encoding/json"
"net/http"
"github.com/karanpratapsingh/tutorials/go/crud/pkg/mocks"
)
func GetAllBooks(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(mocks.Books)
}
Let's start our server and try it out in Postman
$ go run cmd/main.go
This should print all the books we have, currently, it should print our mock books
Add a new Book
Let's add our endpoint to cmd/main.go
router.HandleFunc("/books/{id}", handlers.GetBook).Methods(http.MethodGet)
Create a new handler pkg/handlers/AddBook.go
In this handler we'll do the following:
- Read to request body
- Append to the Book mocks
- Send a
201 created
response
package handlers
import (
"encoding/json"
"io/ioutil"
"log"
"math/rand"
"net/http"
"github.com/karanpratapsingh/tutorials/go/crud/pkg/mocks"
"github.com/karanpratapsingh/tutorials/go/crud/pkg/models"
)
func AddBook(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 book models.Book
json.Unmarshal(body, &book)
// Append to the Book mocks
book.Id = rand.Intn(100)
mocks.Books = append(mocks.Books, book)
// Send a 201 created response
w.WriteHeader(http.StatusCreated)
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode("Created")
}
Let's start our server and try it out in Postman
$ go run cmd/main.go
We should be able to add a new book by providing json
body
Get a Book by Id
Let's add our endpoint to cmd/main.go
router.HandleFunc("/books", handlers.AddBook).Methods(http.MethodPost)
Create a new handler pkg/handlers/GetBook.go
In this handler we'll do the following:
- Read dynamic id parameter
- Iterate over all the mock books
- If ids are equal send book as a response
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/karanpratapsingh/tutorials/go/crud/pkg/mocks"
)
func GetBook(w http.ResponseWriter, r *http.Request) {
// Read dynamic id parameter
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
// Iterate over all the mock books
for _, book := range mocks.Books {
if book.Id == id {
// If ids are equal send book as a response
w.WriteHeader(http.StatusOK)
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(book)
break
}
}
}
Let's start our server and try it out in Postman
$ go run cmd/main.go
Update a Book by Id
Let's add our endpoint to cmd/main.go
router.HandleFunc("/books/{id}", handlers.UpdateBook).Methods(http.MethodPut)
Create a new handler pkg/handlers/UpdateBook.go
In this handler we'll do the following:
- Read dynamic id parameter
- Read request body
- Iterate over all the mock Books
- Update and send a response when book Id matches dynamic Id
package handlers
import (
"encoding/json"
"io/ioutil"
"log"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/karanpratapsingh/tutorials/go/crud/pkg/mocks"
"github.com/karanpratapsingh/tutorials/go/crud/pkg/models"
)
func UpdateBook(w http.ResponseWriter, r *http.Request) {
// Read dynamic id parameter
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
// Read request body
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatalln(err)
}
var updatedBook models.Book
json.Unmarshal(body, &updatedBook)
// Iterate over all the mock Books
for index, book := range mocks.Books {
if book.Id == id {
// Update and send a response when book Id matches dynamic Id
book.Title = updatedBook.Title
book.Author = updatedBook.Author
book.Desc = updatedBook.Desc
mocks.Books[index] = book
w.WriteHeader(http.StatusOK)
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode("Updated")
break
}
}
}
Let's start our server and try it out in Postman
$ go run cmd/main.go
Delete a Book by Id
Let's add our endpoint to cmd/main.go
router.HandleFunc("/books/{id}", handlers.DeleteBook).Methods(http.MethodDelete)
Create a new handler pkg/handlers/DeleteBook.go
In this handler we'll do the following:
- Read the dynamic id parameter
- Iterate over all the mock Books
- Delete book and send a response if the book Id matches dynamic Id
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/karanpratapsingh/tutorials/go/crud/pkg/mocks"
)
func DeleteBook(w http.ResponseWriter, r *http.Request) {
// Read the dynamic id parameter
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
// Iterate over all the mock Books
for index, book := range mocks.Books {
if book.Id == id {
// Delete book and send a response if the book Id matches dynamic Id
mocks.Books = append(mocks.Books[:index], mocks.Books[index+1:]...)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode("Deleted")
break
}
}
}
Let's start our server and try it out in Postman
$ go run cmd/main.go
Next steps
So, we built a basic CRUD API with Go! Our next step could be to connect our API with a real DB like PostgreSQL, which we will look into in the next part!
As mentioned above, the code is available here
I hope this was helpful, as always feel free to reach out if you face any issues.
Have a great day!
This content originally appeared on DEV Community and was authored by Karan Pratap Singh
Karan Pratap Singh | Sciencx (2021-09-01T12:00:22+00:00) Build a REST API with Go – For Beginners. Retrieved from https://www.scien.cx/2021/09/01/build-a-rest-api-with-go-for-beginners/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.