DEV Community

Cover image for Build a REST API with Go - For Beginners
Karan Pratap Singh
Karan Pratap Singh

Posted on Edited on

Build a REST API with Go - For Beginners

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
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

Let's create our main.go

$ touch main.go
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

I will also install Mux which will help us with routing.

GitHub logo gorilla / mux

Package gorilla/mux is a powerful HTTP router and URL matcher for building Go web servers with 🦍

gorilla/mux

testing codecov godoc sourcegraph

Gorilla Logo

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 standard http.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 like…

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)
}
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

Now let's run our app!

$ go run main.go
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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)
}

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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"`
}
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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",
    },
}
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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)
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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)
}

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

Let's start our server and try it out in Postman

$ go run cmd/main.go
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

This should print all the books we have, currently, it should print our mock books

get-books

Add a new Book

Let's add our endpoint to cmd/main.go

router.HandleFunc("/books", handlers.AddBook).Methods(http.MethodPost)
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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.Header().Add("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode("Created")
}
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

Let's start our server and try it out in Postman

$ go run cmd/main.go
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

We should be able to add a new book by providing json body

add-book

Get a Book by Id

Let's add our endpoint to cmd/main.go

router.HandleFunc("/books/{id}", handlers.GetBook).Methods(http.MethodGet)
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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.Header().Add("Content-Type", "application/json")
            w.WriteHeader(http.StatusOK)

            json.NewEncoder(w).Encode(book)
            break
        }
    }
}
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

Let's start our server and try it out in Postman

$ go run cmd/main.go
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

get-book

Update a Book by Id

Let's add our endpoint to cmd/main.go

router.HandleFunc("/books/{id}", handlers.UpdateBook).Methods(http.MethodPut)
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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.Header().Add("Content-Type", "application/json")
            w.WriteHeader(http.StatusOK)

            json.NewEncoder(w).Encode("Updated")
            break
        }
    }
}
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

Let's start our server and try it out in Postman

$ go run cmd/main.go
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

update-book

Delete a Book by Id

Let's add our endpoint to cmd/main.go

router.HandleFunc("/books/{id}", handlers.DeleteBook).Methods(http.MethodDelete)
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

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.Header().Add("Content-Type", "application/json")
            w.WriteHeader(http.StatusOK)
            json.NewEncoder(w).Encode("Deleted")
            break
        }
    }
}
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

Let's start our server and try it out in Postman

$ go run cmd/main.go
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

delete-book

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!

Top comments (12)

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
konjoinfinity profile image
Wesley Scholl •

Thanks for putting this together, simple and straightforward. I was able to turn it into a simple full crud react native app!

github.com/konjoinfinity/rngo

Looking forward to part 2, thanks again.

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
stravid87 profile image
John Stravid •

I always appreciate concise, helpful tutorials that just work. Thank you!

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
karimelngr profile image
Karim ElNaggar •

Thanks for the tutorial, I think there is a small bug here

w.WriteHeader(http.StatusOK)
w.Header().Add("Content-Type", "application/json")
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

should be

w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

otherwise the content type header does not get written
Another thing, I think you switched two lines

router.HandleFunc("/books/{id}", handlers.GetBook).Methods(http.MethodGet)
// and
router.HandleFunc("/books", handlers.AddBook).Methods(http.MethodPost)
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini

between the two sections Add a new Book and Get a book by id

Thanks again for the great tutorial

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
karanpratapsingh profile image
Karan Pratap Singh •

Nice catch! Thanks, I have updated

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
sethjoe12 profile image
karl joseph •

hai sir good day can you help me with this thing .it was a code structure sir.

i edited all your codes and it comes with this error

"..\pkg\test\Additem.go:24:19: cannot use item (type models.Item) as type []byte in argument to json.Unmarshal"

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
cerchie profile image
Lucia Cerchie •

This was a great article! will there be a part 2 soon?

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
karanpratapsingh profile image
Karan Pratap Singh •

Hi Lucia, I'm glad this was helpful. yes there will be a part 2 where we will connect our API with gorm!

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
karanpratapsingh profile image
Karan Pratap Singh •
πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini Thread
 
cerchie profile image
Lucia Cerchie •

this is awesome Karan! thanks!

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini Thread
 
karanpratapsingh profile image
Karan Pratap Singh •

Thanks!

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
ramonivandy profile image
Ramon Ivandy Setiawan •

Thank you for the tutorial!

But in Get All Books, there is a typo that confuse me a minute πŸ˜… It should GetAllBooks.go instead GetBooks.go right?

Image description

πŸ“Ί Info Siaran: karanpratapsingh/build a rest api with go for beginners gp - Pertandingan Hari Ini
 
shaijut profile image
Shaiju T •

I can see Go used by many Organization in cloud. But readability is not developer friendly like C# or Java. Have you tried Asp.net Core Web API to build API's its clean and fast ?