---
title: How to build an API with Go and Gin
published: "2024-04-02"
publisher: Honeybadger
author: Jeffery Morhous
category: Go articles
tags:
  - Go
  - Gin
description: "Learn how to build a simple grocery management API using Go and Gin! We'll create a straightforward grocery item model and use Gin to build the CRUD API."
url: "https://www.honeybadger.io/blog/go-and-gin-api/"
---

Have you ever found yourself rummaging through the fridge, trying to figure out what groceries you have and what you need to buy?

Okay, maybe this isn't a super serious problem, but what better way to manage this chore than by building a web API? In this guide, we'll craft a grocery management API with Go and Gin.

If you're new to API lingo, CRUD might sound a bit ... well, crude. However, it's a fundamental concept! CRUD stands for create, read, update, and delete. These represent the basic operations you'll need for persistent storage. Much of the internet can be modeled this way.

Gin is a blazing-fast web framework for Go. In addition to its performance, Gin is known for ease of integration with middleware like logging and error handling. Beyond that, it's concise and intuitive, so it's approachable as a newcomer.

By the end of this article, you'll have a fully-functional API capable of managing your grocery list, all built with Go and Gin.

## Setting up your Go development environment

Let's get the ball rolling by ensuring our local machine is dressed up for the Go party. 🎉 If you're used to programming in other languages, setting up Go will feel familiar.

### Installing Go

To begin, head over to the official Go downloads page and grab the installer for your operating system. Run it, and within a few clicks, you'll have Go installed.

You can verify Go is installed by checking the version. In your command line, run the following:

```bash
go version
```

On my machine, this returns the following:

```bash
go version go1.20.4 darwin/arm64
```

### Installing Gin

We'll use the Gin web framework for this project, and you can install it with Go's built-in package manager. First, create a new directory to house the API we'll create:

```bash
mkdir go-gin-api
```

You can substitute `go-gin-api` with whatever you'd like to name the project. Next, cd into the directory and initialize a new Go project:

```bash
go mod init go-gin-api
```

Again, you can substitute `go-gin-api` here for your chosen name. Lastly, install Gin:

```bash
go get -u github.com/gin-gonic/gin
```

## Designing the grocery inventory model

Before we jump into all the routing and handling magic, let's lay down the foundation. The core of our grocery management API lies in how we represent a grocery item. Like all object-oriented programming, we'll represent a concept with an object, or model. It's a bit more involved than scribbling items on a piece of paper, but this is the digital age!

Our "Grocery Item" model will have three attributes: name, count, and category. For the sake of this project, we'll store our objects in an in-memory array instead of a database. If you're looking to scale this or have better persistence, then consider replacing it with a SQL database.

First, create a directory named `models` in the root of your project. In this directory, create a file titled `groceryItem.go`. In this file, we’ll write the model:

```go
package models type GroceryItem struct { ID       int    `json:"id"` Name     string `json:"name"` Count    int    `json:"count"` Category string `json:"category"` }
```

Next, create a directory called `data` in the root of your repo. In this directory, create a file called `store.go`. In this file, we'll set up our array that stores GroceryItems.

```go
package data import "go-gin-api/models" // Our in-memory 'database' of grocery items. var GroceryDB = []models.GroceryItem{} // A simple counter for our IDs, to simulate auto-increment since we're not using a real DB. var IDCounter = 1
```

With our model and data store in place, we can move on to accepting incoming web requests!

## Building the CRUD endpoints

Before writing the code to handle data, create a file named `main.go` in the root of your project that will serve as an entry point for incoming requests. Here's what we'll start with:

```go
package main import ( "go-gin-api/handlers" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() // Register the route to our handler function r.POST("/groceries", handlers.AddGroceryItem) r.Run() // default listens on :8080 }
```

This file imports our handlers directory, which we'll write next, as well as the gin framework. In the main function, we create a route to handle POST requests to the `/groceries` endpoint and set the handler to a file we'll create next.

### Create

Let's start by ensuring our digital pantry can receive new stock (or, in layman's terms, add items to the data store).

We'll use a POST request to /groceries to symbolize the addition of a new item. It's intuitive and in line with common REST practices. We also want to ensure that the data coming in is not only readable but also valid. This includes making sure essential fields are filled and that the data types are correct. Once we've done that, we'll construct a new grocery item and append it to our in-memory database.

Create a new directory in the root of your project named `handlers`, and in it, create a file called `groceryHandlers.go`. Here's what we'll start with:

```go
package handlers import ( "net/http" "strconv" "go-gin-api/data" "go-gin-api/models" "github.com/gin-gonic/gin" ) func AddGroceryItem(c *gin.Context) { var newItem models.GroceryItem if err := c.ShouldBindJSON(&newItem); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Assign a new ID and append to the database newItem.ID = data.IDCounter data.GroceryDB = append(data.GroceryDB, newItem) data.IDCounter++ c.JSON(http.StatusCreated, newItem) }
```

With this code, our API can now accept new grocery items and give them a unique ID.

### Read

Storing information about grocery items isn't particularly useful if we can't retrieve it. Next, we'll create the endpoints for reading grocery items from the system. First, we'll define an endpoint to fetch the details of a specific grocery item, and then we'll define an endpoint to list all grocery items.

To get a specific item, we'll set up a GET request to /groceries/:id, where :id is a dynamic parameter representing the unique ID of the grocery item.

In your `main.go` file, you will insert additional routes for these two endpoints. Append the following code in this file below your first route:

```go
// New GET routes r.GET("/groceries", handlers.ListGroceryItems) r.GET("/groceries/:id", handlers.GetGroceryItem)
```

Next, we'll add our handler code to `handlers/groceryHandlers.go`. In this file, add the following functions:

```go
// Fetch a specific grocery item by ID func GetGroceryItem(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) for _, item := range data.GroceryDB { if item.ID == id { c.JSON(http.StatusOK, item) return } } c.JSON(http.StatusNotFound, gin.H{"message": "Item not found"}) } // List all grocery items func ListGroceryItems(c *gin.Context) { c.JSON(http.StatusOK, data.GroceryDB) }
```

With these routes in place, we can now fetch individual items by their ID or get a comprehensive list of all items.

### Update

When it comes to updating, we need to ensure we're pinpointing the right item and making the intended modifications.

A PUT request to /groceries/:id follows best practices. The :id parameter will identify the item we intend to update. Our handler will locate the item by its ID in our in-memory datastore, and then update the relevant fields based on the provided input.

In `main.go`, add the new route:

```go
r.PUT("/groceries/:id", handlers.UpdateGroceryItem)
```

Next, we'll add the handler for this endpoint to `handlers/groceryHandlers.go`. Add the following function to the file:

```go
// Update a specific grocery item by ID func UpdateGroceryItem(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var updatedItem models.GroceryItem if err := c.ShouldBindJSON(&updatedItem); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } for index, item := range data.GroceryDB { if item.ID == id { data.GroceryDB[index].Name = updatedItem.Name data.GroceryDB[index].Count = updatedItem.Count data.GroceryDB[index].Category = updatedItem.Category c.JSON(http.StatusOK, data.GroceryDB[index]) return } } c.JSON(http.StatusNotFound, gin.H{"message": "Item not found"}) }
```

With this PUT endpoint, we can now modify an item's name, count, or category based on its ID. Remember, while our in-memory database makes the process straightforward, using a real database will likely introduce additional complexities.

### Delete

Building an endpoint to allow deletion of a grocery item is pretty straightforward.

For deletion, a DELETE request to /groceries/:id follows best practices. As before, the :id parameter will point us to the item that's up for removal. The handler will simply identify the item by its ID in our in-memory database and remove it.

First, add the new route. In `main.go`, add the following to the list of routes:

```go
// New DELETE route r.DELETE("/groceries/:id", handlers.DeleteGroceryItem)
```

Next, we'll add the new handler. In `handlers/groceryHandlers.go`, add the following new function:

```go
// Delete a specific grocery item by ID func DeleteGroceryItem(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) for index, item := range data.GroceryDB { if item.ID == id { // Remove item from our "database" data.GroceryDB = append(data.GroceryDB[:index], data.GroceryDB[index+1:]...) c.JSON(http.StatusOK, gin.H{"message": "Item deleted successfully"}) return } } c.JSON(http.StatusNotFound, gin.H{"message": "Item not found"}) }
```

## Conclusion

Now that all the CRUD operations are in place, it's time to give our grocery application a spin.

You can start the application by running the following:

```bash
go run main.go
```

This command compiles and starts your application. If everything is configured correctly, you will see a message stating that your server is running:

```bash
[GIN-debug] Listening and serving HTTP on :8080
```

You can now use your browser, CURL, or a tool like postman to test out the app!

In this article, we've walked through how to use Go and Gin to build a performant CRUD API that keeps track of your grocery inventory! While limited in scope, this represents solving a very real problem. If you're interested in scaling the application beyond this initial scope, consider [adding a database](https://www.honeybadger.io/blog/how-to-create-crud-application-with-golang-and-mysql/), expanding on error validation, and even writing unit tests! Have fun exploring Go and Gin!

---

## Try Honeybadger for FREE

Intelligent logging, error tracking, and Just Enough APM™ in one dev-friendly platform. Find and fix problems before users notice.

[Start free trial](https://app.honeybadger.io/users/sign_up)

[See plans and pricing](https://www.honeybadger.io/plans/)
