Building a RESTful API in Go with Gin Framework: CRUD Operations for a Recipe Management System

Building a RESTful API in Go with Gin Framework: CRUD Operations for a Recipe Management System

Sundaram Kumar JhaSundaram Kumar Jha

Introduction

This blog will guide you through building a RESTful API using the Gin framework in Golang to manage recipes and orders. We'll cover various CRUD operations like creating, reading, updating, and deleting orders. This API reads data from a JSON file, processes it, and returns or modifies it based on the API requests. If you're looking to create a lightweight, efficient, and easy-to-use API server in Go, this tutorial is for you.

Prerequisites

Before starting, ensure you have the following:

  • Go: Installed on your machine (version 1.16 or above recommended).

  • Gin Framework: A fast and lightweight web framework for building HTTP servers in Go.

  • Basic Go knowledge: Understanding of Go syntax and packages like net/http, encoding/json, etc.

For the sake of simplicity i have coded api in one file but you can choose to take these into their specific modules and also you can add database of your own (here we have used json files)

Getting Started

Let's start by creating a new Go project. Initialize a new directory with go mod init your-module-name.

go mod init recipe-api

Next, install the Gin framework:

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

Create a file named main.go and paste the following code into it:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"

    "github.com/gin-gonic/gin"
)

// MenuItem represents a single recipe item with its details
type MenuItem struct {
    Item   string  `json:"item"`
    Recipe string  `json:"recipe"`
    Price  float64 `json:"price"`
}

// Order represents a customer's order, containing a list of item names
type Order struct {
    Order []string `json:"orders"`
}

var total float64

// GetRecipes retrieves all available recipes from a JSON file
func GetRecipes(c *gin.Context) {
    var items []MenuItem

    data, err := ioutil.ReadFile("recipes.json")
    if err != nil {
        c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
        return
    }

    if err = json.Unmarshal(data, &items); err != nil {
        c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, items)
}

// CreateOrder processes a new order and calculates the total cost
func CreateOrder(c *gin.Context) {
    var orders Order

    if err := c.ShouldBindJSON(&orders); err != nil {
        c.JSON(http.StatusBadGateway, gin.H{"Error": err.Error()})
        return
    }

    var items []MenuItem

    data, err := ioutil.ReadFile("recipes.json")
    if err != nil {
        c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
        return
    }

    if err = json.Unmarshal(data, &items); err != nil {
        c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
        return
    }

    // Create a map to store item prices for quick lookup
    priceMap := make(map[string]float64)
    for _, item := range items {
        priceMap[item.Item] = item.Price
    }

    // Calculate the total price
    total = 0
    for _, orderItem := range orders.Order {
        if price, exists := priceMap[orderItem]; exists {
            total += price
        }
    }

    c.JSON(http.StatusOK, gin.H{
        "total": total,
    })
}

// UpdateOrder updates the current order by recalculating the total
func UpdateOrder(c *gin.Context) {
    CreateOrder(c)
}

// GetOrder retrieves the current orders
func GetOrder(c *gin.Context) {
    var orders *Order
    c.JSON(http.StatusOK, gin.H{
        "orders": orders,
    })
}

// DeleteOrder removes an item from the order and recalculates the total
func DeleteOrder(c *gin.Context) {
    var orders *Order

    if orders == nil {
        orders = &Order{}
    }

    // Get the item name from the URL parameter
    itemToDelete, err := url.QueryUnescape(c.Param("item"))
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid item name"})
        return
    }

    var index = -1

    for i, orderItem := range orders.Order {
        if orderItem == itemToDelete {
            index = i
            break
        }
    }

    orders.Order = append(orders.Order[:index], orders.Order[index+1:]...)

    var items []MenuItem

    // Create a map to store item prices for quick lookup
    priceMap := make(map[string]float64)
    for _, item := range items {
        priceMap[item.Item] = item.Price
    }

    c.JSON(http.StatusOK, gin.H{
        "total": total,
    })
}

// main sets up the routes and starts the Gin server
func main() {
    routes := gin.Default()

    routes.GET("/", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "welcome to recipes api"})
    })

    routes.GET("/recipes", GetRecipes)
    routes.POST("/orders", CreateOrder)
    routes.PUT("/orders", UpdateOrder)
    routes.DELETE("/orders/:item", DeleteOrder)

    fmt.Println("server is running on port 8080...")
    routes.Run(":8080")
}

Key Features

  • CRUD Operations: Create, read, update, and delete orders.

  • JSON Parsing: Efficiently handle JSON data using the encoding/json package.

  • Error Handling: Properly handle errors to ensure the API is robust.

  • Gin Framework: A fast, lightweight web framework that simplifies HTTP handling in Go.

API Endpoints

  1. GET /recipes: Retrieves all available recipes from recipes.json.

  2. POST /orders: Creates a new order and calculates the total price.

  3. PUT /orders: Updates an existing order by recalculating the total price.

  4. DELETE /orders/:item: Deletes an item from the order and updates the total.

Running the Application

  1. Ensure your recipes.json file is in the same directory as main.go. Here's an example of what your JSON file might look like:
[
  {"item": "Pasta", "recipe": "Boil pasta and add sauce", "price": 10.99},
  {"item": "Pizza", "recipe": "Bake pizza dough with toppings", "price": 15.99}
]
  1. Run the server:
go run main.go
  1. The server will start on http://localhost:8080. You can use tools like Postman or cURL to interact with the API.

Conclusion

Building a RESTful API with the Gin framework in Go is both straightforward and efficient. This tutorial provides a foundational example that you can expand upon to suit your specific requirements. Whether you're managing recipes or building another type of CRUD-based application, the principles covered here will serve as a solid starting point.