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 t…


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.

GitHub logo gorilla / mux

A powerful HTTP router and URL matcher for building Go web servers with ?

gorilla/mux

GoDoc CircleCI Sourcegraph

Gorilla Logo

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 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…

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

get-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

add-book

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

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)

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

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)

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

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!


This content originally appeared on DEV Community and was authored by Karan Pratap Singh


Print Share Comment Cite Upload Translate Updates
APA

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/

MLA
" » Build a REST API with Go – For Beginners." Karan Pratap Singh | Sciencx - Wednesday September 1, 2021, https://www.scien.cx/2021/09/01/build-a-rest-api-with-go-for-beginners/
HARVARD
Karan Pratap Singh | Sciencx Wednesday September 1, 2021 » Build a REST API with Go – For Beginners., viewed ,<https://www.scien.cx/2021/09/01/build-a-rest-api-with-go-for-beginners/>
VANCOUVER
Karan Pratap Singh | Sciencx - » Build a REST API with Go – For Beginners. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/09/01/build-a-rest-api-with-go-for-beginners/
CHICAGO
" » Build a REST API with Go – For Beginners." Karan Pratap Singh | Sciencx - Accessed . https://www.scien.cx/2021/09/01/build-a-rest-api-with-go-for-beginners/
IEEE
" » Build a REST API with Go – For Beginners." Karan Pratap Singh | Sciencx [Online]. Available: https://www.scien.cx/2021/09/01/build-a-rest-api-with-go-for-beginners/. [Accessed: ]
rf:citation
» Build a REST API with Go – For Beginners | Karan Pratap Singh | Sciencx | 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.

You must be logged in to translate posts. Please log in or register.