How to build a REST API with Golang using Gin and Gorm
Tags: tutorial, webdev, go
Published: 2020-02-27 17:28:01

---
title: How to build a REST API with Golang using Gin and Gorm
published: true
date: 2020-02-25 17:00:49 UTC
tags: tutorial,webdev,go 
canonical_url: https://blog.logrocket.com/how-to-build-a-rest-api-with-golang-using-gin-and-gorm/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/3dqy36p1xm0adgti904l.png
---
**Written by [Rahman Fadhil](https://blog.logrocket.com/author/rahmanfadhil/)**✏️

Go is a very popular language for good reason. It offers similar performance to other “low-level” programming languages such as Java and C++, but it’s also incredibly simple, which makes the development experience delightful.

What if we could combine a fast programming language with a speedy web framework to build a high-performance RESTful API that can handle a crazy amount of traffic?

After doing a lot of research to find a fast and reliable framework for this beast, I came across a fantastic open-source project called [Gin](https://github.com/gin-gonic/gin). This framework is lightweight, well-documented, and, of course, extremely fast.

Unlike other Go web frameworks, Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter), which means it can navigate through your API routes faster than most frameworks out there. The creators also claim it can run 40 times faster than [Martini](https://github.com/go-martini/martini), a relatively similar framework to Gin. You can see a more detailed comparison in this [benchmark](https://github.com/gin-gonic/gin/blob/master/BENCHMARKS.md).

Although it may seem like the Holy Grail at first glance, this stack may or may not be the best option for your project, depending on the scenario.

Gin is a microframework that doesn’t come with a ton of fancy features out of the box. It only gives you the essential tools to build an API, such as routing, form validation, etc. So for tasks such as authenticating users, uploading files, and sending emails, you need to either install another third-party library or implement them yourself.

This can be a huge disadvantage for a small team of developers that needs to ship a lot of features in a very short time. Another web framework, such as [Laravel](https://laravel.com) and [Ruby on Rails](https://rubyonrails.org), might be more appropriate for such a team. Such frameworks are opinionated, easier to learn, and provide a lot of features out of the box, which enables you to develop a fully functioning web application in an instant.

If you’re part of a small team, this stack may be overkill. But if you have the appetite to make a long-term investment, you can really take advantage of the extraordinary performance and flexibility of Gin.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## What we will build

In this tutorial, we’ll demonstrate how to build a bookstore REST API that provides book data and performs CRUD operations.

Before we get begin, I’ll assume that you:

- Have Go installed on your machine
- Understand the Go language
- Have a general understanding of RESTful API

Let’s start by initializing a new [Go module](https://blog.golang.org/using-go-modules). This will enable us to manage the dependencies that are specifically installed for this project. Make sure you run this command inside your [Go environment folder](https://github.com/golang/go/wiki/GOPATH).

```jsx
$ go mod init
```

Now let’s install some dependencies.

```jsx
go get github.com/gin-gonic/gin github.com/jinzhu/gorm
```

After the installation is complete, your folder should contain two files: `mod.mod` and `go.sum`. Both of these files contain information about the packages you installed, which is helpful when working with other developers. If somebody wants to contribute to the project, all they need to do is run the `go mod download` command on their terminal to install all the required dependencies on their machine.

For reference, I published the entire source code of this project on my [GitHub](https://github.com/rahmanfadhil/gin-bookstore). Feel free to poke around or clone it onto your computer.

```jsx
$ git clone https://github.com/rahmanfadhil/gin-bookstore.git
```

## Setting up the server

Let’s start by creating a Hello World server inside the `main.go` file.

```jsx
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"
)

func main() {
  r := gin.Default()

  r.GET("/", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"data": "hello world"})    
  })

  r.Run()
}
```

We first need to declare the `main` function that will be triggered whenever we run our application. Inside this function, we’ll initialize a new Gin router within the `r` variable. We’re using the `Default` router because Gin provides some useful middlewares we can use to debug our server.

Next, we’ll define a `GET` route to the `/` endpoint. If you’ve worked with other frameworks, such as [Express.js](https://expressjs.com), [Flask](https://www.palletsprojects.com/p/flask), or [Sinatra](http://sinatrarb.com), you should be familiar with this pattern.

To define a route, we need to specify two things: the endpoint and the handler. The endpoint is the path the client wants to fetch. For instance, if the user wants to grab all books in our bookstore, they’d fetch the `/books` endpoint. The handler, on the other hand, determines how we provide the data to the client. This is where we put our business logic, such as grabbing the data from the database, validating the user input, and so on.

We can send several types of response to the client, but RESTful APIs typically give the response in JSON format. To do that in Gin, we can use the `JSON` method provided from the request context. This method requires an [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) and a JSON response as the parameters.

Lastly, we can run our server by simply invoking the `Run` method of our Gin instance.

To test it out, we’ll start our server by running the command below.

```jsx
$ go run main.go
```

## Setting up the database

The next step is to build our database models.

A model is a class (or a struct in Go) that enables us to communicate with a specific table in our database. In Gorm, we can create our model by defining a Go struct. This model will contain the properties that represent fields in our database table.

Since we’re trying to build a bookstore API, let’s create a `Book` model.

```jsx
// models/book.go

package models

import (
  "github.com/jinzhu/gorm"
)

type Book struct {
  ID     uint   `json:"id" gorm:"primary_key"`
  Title  string `json:"title"`
  Author string `json:"author"`
}
```

Our `Book` model is pretty straightforward; each book has a title and an author name, which has a string data type and a unique ID number to differentiate each book in the database.

We’ll also specify the [tags](https://golang.org/ref/spec#Struct_types) on each field using backtick annotation. This allows us to map each field into a different name when we send them as a response, since JSON and Go have different naming conventions.

To better organize our code, we can put this code inside a separate module called `models`.

Now let’s create a utility function called `SetupModels`, which allows us to create a connection with our database and migrate our model’s schema. We can put this inside the `setup.go` file in our `models` module.

```jsx
// models/setup.go

package models

import (
  "github.com/jinzhu/gorm"
  _ "github.com/jinzhu/gorm/dialects/sqlite"
)

func SetupModels() *gorm.DB {
  db, err := gorm.Open("sqlite3", "test.db")

  if err != nil {
    panic("Failed to connect to database!")
  }

  db.AutoMigrate(&Book{})

  return db
}
```

Inside this function, we’ll create a new connection with `gorm.Open` method, where we’ll specify what kind of database we plan to use and how to access it. Currently, Gorm only supports four types of SQL databases. For our purposes, we’ll use [SQLite](https://www.sqlite.org/index.html) and store our data inside the `test.db` file.

To connect our server to the database, we need to import the database’s driver, which is located inside the `github.com/jinzhu/gorm/dialects` module.

We also need to check whether the connection was created successfully. If not, it will print out the error to the console and terminate the server.

Next, we’ll migrate the database schema by using `AutoMigrate`. Be sure to call this method on each model you created.

We can call this function in our `main.go` file.

```jsx
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"

  "github.com/rahmanfadhil/gin-bookstore/models" // new
)

func main() {
  r := gin.Default()

  db := models.SetupModels() // new

  r.Run()
}
```

## RESTful Routes

We’re almost there!

The last thing we need to do is to implement our controllers. In the previous section, we learned how to create a route handler (i.e., controller) inside our `main.go` file. However, this approach makes our code much harder to maintain. Instead, we can put our controllers inside a separate module called `controllers`.

But before we do that, we need to create a middleware that can provide the database instance to every single controller since they live in another file that can’t access the database instance directly.

```jsx
// main.go

// ...

func main() {
  r := gin.Default()

  db := models.SetupModels()

  // Provide db variable to controllers
  r.Use(func(c *gin.Context) {
    c.Set("db", db)
    c.Next()
  })

  r.Run()
}
```

Middleware is basically a function that interferes with the client’s request. There are myriad benefits of using middleware. For instance, it enables you to provide data to the next route handler whenever a request comes in.

In this case, we are providing the `db` variable by using the `Set` method from the context. Be sure to call the next handler using the `Next` method. Otherwise, your API will do nothing when you send a request.

```jsx
// controllers/books.go

package controllers

import (
  "github.com/gin-gonic/gin"
  "github.com/rahmanfadhil/gin-bookstore/models"
)

// GET /books
// Get all books
func FindBooks(c *gin.Context) {
  db := c.MustGet("db").(*gorm.DB)

  var books []models.Book
  db.Find(&books)

  c.JSON(http.StatusOK, gin.H{"data": books})
}
```

Here, we have a `FindBooks` function that will return all books from our database. In the first line of our function, we’re trying to get the database instance we provide in our middleware so that we can use it to fetch our books and return it to the client. We also need to import our `models` module at the top so we can reference our `Book` model in each controller.

Next, we’ll register our function as a route handler in `main.go`.

```jsx
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"

  "github.com/rahmanfadhil/gin-bookstore/models"
  "github.com/rahmanfadhil/gin-bookstore/controllers" // new
)

func main() {
  r := gin.Default()

  db := models.SetupModels()

  r.Use(func(c *gin.Context) {
    c.Set("db", db)
    c.Next()
  })

  r.GET("/books", controllers.FindBooks) // new

  r.Run()
}
```

Pretty simple, right?

Make sure you add this line before the database middleware. Otherwise, your controller won’t be able to access your database instance.

Now let’s run your server and hit the `/books` endpoint.

```jsx
{
  "data": []
}
```

If an empty array shows as the result, it means your applications are working. We get this result because we haven’t created a book yet. Let’s create a create book controller.

To create a book, we need to have a schema that can validate the user’s input to prevent us from getting invalid data.

```jsx
type CreateBookInput struct {
  Title  string `json:"title" binding:"required"`
  Author string `json:"author" binding:"required"`
}
```

The schema is very similar to our model. We don’t need the `ID` property since the database will generate it automatically.

Now we can use that schema in our controller.

```jsx
// POST /books
// Create new book
func CreateBook(c *gin.Context) {
  db := c.MustGet("db").(*gorm.DB)

  // Validate input
  var input CreateBookInput
  if err := c.ShouldBindJSON(&input); err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }

  // Create book
  book := models.Book{Title: input.Author, Author: input.Author}
  db.Create(&book)

  c.JSON(http.StatusOK, gin.H{"data": book})
}
```

We can now validate the request body using the `ShouldBindJSON` method and pass the schema. If the data is invalid, it will return a `400` error to the client and tell them which fields are invalid. Otherwise, it will create a new book, save it to the database, and return it.

Now it’s time to add the `CreateBook` controller in `main.go`.

```jsx
func main() {
  // ...

  r.GET("/books", controllers.FindBooks)
  r.POST("/books", controllers.CreateBook) // new
}
```

Let’s send a POST request to the `/books` endpoint with the following request body.

```jsx
{
  "title": "Start with Why",
  "author": "Simon Sinek"
}


The response should look like this:
```

```jsx
{
  "data": {
    "id": 1,
    "title": "Start with Why",
    "author": "Simon Sinek"
  }
}
```

Now that we’ve successfully created our first book, let’s add a controller that can fetch a single book.

```jsx
// GET /books/:id
// Find a book
func FindBook(c *gin.Context) {
  db := c.MustGet("db").(*gorm.DB)

  // Get model if exist
  var book models.Book
  if err := db.Where("id = ?", c.Param("id")).First(&book).Error; err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
    return
  }

  c.JSON(http.StatusOK, gin.H{"data": book})
}
```

Our `FindBook` controller is pretty similar to the `FindBooks` controller, except that we only get the first book that matches the ID we got from the request parameter. We also need to check wether the book exists by simply wrapping it inside an `if` statement.

Next, register it into your `main.go` file.

```jsx
func main() {
  // ...

  r.GET("/books", controllers.FindBooks)
  r.POST("/books", controllers.CreateBook)
  r.GET("/books/:id", controllers.FindBook) // new
}
```

To get the `id` parameter, we need to specify it from the route path, as shown above.

Now let’s run the server and fetch `/books/1` to get the book we just created.

```jsx
{
  "data": {
    "id": 1,
    "title": "Start with Why",
    "author": "Simon Sinek"
  }
}
```

So far, so good. Now let’s add the `UpdateBook` controller to update an existing book. But before we do, we must define the schema for validating the user input first.

```jsx
struct UpdateBookInput {
  Title  string `json:"title"`
  Author string `json:"author"`  
}
```

The `UpdateBookInput` schema is pretty much the same as our `CreateBookInput`, except we don’t need to make those fields required because the user doesn’t have to fill all the properties of the book.

Use the following code to add the controller.

```jsx
// PATCH /books/:id
// Update a book
func UpdateBook(c *gin.Context) {
  db := c.MustGet("db").(*gorm.DB)

  // Get model if exist
  var book models.Book
  if err := db.Where("id = ?", c.Param("id")).First(&book).Error; err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
    return
  }

  // Validate input
  var input UpdateBookInput
  if err := c.ShouldBindJSON(&input); err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }

  db.Model(&book).Updates(input)

  c.JSON(http.StatusOK, gin.H{"data": book})
}
```

First, we’ll copy the code from the `FindBook` controller to grab a single book and make sure it exists. After we find the book, we need to validate the user input with the `UpdateBookInput` schema. Finally, we’ll update the book model using the `Updates` method and return the updated book data to the client.

Register it into your `main.go`.

```jsx
func main() {
  // ...

  r.GET("/books", controllers.FindBooks)
  r.POST("/books", controllers.CreateBook)
  r.GET("/books/:id", controllers.FindBook)
  r.PATCH("/books/:id", controllers.UpdateBook) // new
}
```

Let’s test it! Fire a `PATCH` request to `/books/:id` endpoint to update the book title.

```jsx
{
  "title": "The Infinite Game"
}
```

The result should be:

```jsx
{
  "data": {
    "id": 1,
    "title": "The Infinite Game",
    "author": "Simon Sinek"
  }
}
```

The last step is to implement the `DeleteBook` feature.

```jsx
// DELETE /books/:id
// Delete a book
func DeleteBook(c *gin.Context) {
  db := c.MustGet("db").(*gorm.DB)

  // Get model if exist
  var book models.Book
  if err := db.Where("id = ?", c.Param("id")).First(&book).Error; err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
    return
  }

  db.Delete(&book)

  c.JSON(http.StatusOK, gin.H{"data": true})
}
```

Just like the update controller, we get the book model from the request parameters (if it exists) and delete it with the `Delete` method from our database instance, which we get from our middleware. Then, return `true` as the result, since there is no reason to return a deleted book back to the client.

```jsx
func main() {
  // ...

  r.GET("/books", controllers.FindBooks)
  r.POST("/books", controllers.CreateBook)
  r.GET("/books/:id", controllers.FindBook)
  r.PATCH("/books/:id", controllers.UpdateBook)
  r.DELETE("/books/:id")
}
```

Let’s test it out by sending a `DELETE` request to the `/books/1` endpoint.

```jsx
{
  "data": true
}
```

If we fetch all books in `/books`, we’ll again see an empty array.

```jsx
{
  "data": []
}
```

## Conclusion

Go offers two major qualities that all developers desire and all programming languages aim to achieve: simplicity and performance. While this technology may not be the best option for every developer team, it’s still a very solid solution and a skill worth learning.

By building this project from scratch, I hope you gained a basic understanding of how to develop a RESTful API with Gin and Gorm, how they work together, and how to implement the CRUD features. There is still plenty of room for improvement, such as authenticating users with JWT, implementing unit testing, containerizing your app with Docker, and a lot of other cool stuff you can mess around with if you want to dig deeper.

* * * 
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *

The post [How to build a REST API with Golang using Gin and Gorm](https://blog.logrocket.com/how-to-build-a-rest-api-with-golang-using-gin-and-gorm/) appeared first on [LogRocket Blog](https://blog.logrocket.com).