Lesson 3
Bulk Uploading Items in the ToDo App
Introduction

Welcome to our lesson on enhancing the ToDo app with a Bulk Uploading feature. The focus of today's session is on improving the app's efficiency by allowing multiple tasks to be added in one go. This functionality is particularly useful when you need to add multiple items simultaneously, saving time and effort.

By the end of this lesson, you’ll understand how to implement the bulk uploading feature in the ToDo app using Gin, a web framework for Go. You will learn how to set up a route, manage requests, and validate data effectively. Let's dive in!

Implementing the Bulk Upload Route

To handle bulk uploading in our app, the first step is to register a new route in the router. The RegisterRoutes function helps us achieve this:

Go
1package router 2 3import ( 4 "github.com/gin-gonic/gin" 5 6 "codesignal.com/todoapp/controllers" 7) 8 9func RegisterRoutes(router *gin.Engine) { 10 // Register the bulk upload route 11 router.POST("/api/todos/bulk", controllers.BulkUploadTodos) 12}

Here is what's happening:

  • We define a POST route /api/todos/bulk, which is titled meaningfully to reflect that it handles bulk operations.
  • We use router.POST to bind the route to the BulkUploadTodos function, ensuring the controller manages incoming requests appropriately.

This structure helps keep the router neat and the responsibility of request handling delegated to the controller, adhering to the principle of separation of concerns.

Handling Requests in the BulkUploadTodos Controller

Once a POST request hits the /api/todos/bulk endpoint, the corresponding controller function, BulkUploadTodos, processes it:

Go
1package controllers 2 3import ( 4 "net/http" 5 6 "github.com/gin-gonic/gin" 7 8 "codesignal.com/todoapp/services" 9 "codesignal.com/todoapp/models" 10 "codesignal.com/todoapp/utils" 11) 12 13var todos = make([]models.Todo, 0) // Initialize an empty todos slice 14 15func BulkUploadTodos(c *gin.Context) { 16 var newTodos []models.Todo 17 18 if err := c.ShouldBindJSON(&newTodos); err != nil { 19 c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON payload"}) 20 return 21 } 22 23 duplicates := utils.CheckForDuplicates(newTodos) 24 if len(duplicates) > 0 { 25 c.JSON(http.StatusBadRequest, gin.H{"error": "Duplicate todos found", "duplicates": duplicates}) 26 return 27 } 28 29 addedTodos := services.AddTodos(&todos, newTodos) 30 c.JSON(http.StatusCreated, addedTodos) 31}

Let's break this down:

  • We use c.ShouldBindJSON(&newTodos) to bind incoming JSON data to a newTodos slice. This method checks the request's body format and validates the JSON structure.
  • If the payload contains invalid JSON, it responds with a 400 Bad Request status.
  • To ensure every todo item is unique, we call utils.CheckForDuplicates(newTodos) to identify any repeating entries.
  • If duplicates are found, a similar 400 Bad Request response is sent, specifying the duplicate titles.
  • Finally, services.AddTodos appends the valid todos, and a 201 Created response is returned with the newly added todos.
Managing Todos in Services

The service layer processes the validated data and adds it to the maintained todo list, assigning unique IDs:

Go
1package services 2 3import "codesignal.com/todoapp/models" 4 5func AddTodos(todos *[]models.Todo, newTodos []models.Todo) []models.Todo { 6 for i := range newTodos { 7 newTodos[i].ID = len(*todos) + 1 + i 8 *todos = append(*todos, newTodos[i]) 9 } 10 return newTodos 11}

Here's how this works:

  • The AddTodos function iterates over newTodos, assigning each a unique ID to maintain data integrity.
  • IDs are computed based on the current length of the todos already stored, ensuring no duplication.
  • Each new todo is appended to the existing todos list, making the updated list available for future requests.
Validating and Checking for Duplicate Entries

Checking for and managing duplicates ensure our data's consistency and prevents unwanted redundancy:

Go
1package utils 2 3import "codesignal.com/todoapp/models" 4 5func CheckForDuplicates(todos []models.Todo) []string { 6 titleMap := make(map[string]bool) 7 duplicates := []string{} 8 9 for _, todo := range todos { 10 if _, found := titleMap[todo.Title]; found { 11 duplicates = append(duplicates, todo.Title) 12 } else { 13 titleMap[todo.Title] = true 14 } 15 } 16 17 return duplicates 18}

This function operates by:

  • Maintaining a map of todo titles, where each entry's existence is checked.
  • Titles found more than once are collected as duplicates.
  • Returning duplicate titles allows our controller to respond with specific information on what entries need corrective attention.
Crafting the Bulk Upload Request

When making a bulk upload request to our ToDo app, the payload should be a JSON array of todo items, each represented as an object with the necessary fields. Note that the ID field is managed by the server and shouldn't be included in the request.

A typical JSON payload might look like this:

JSON
1[ 2 { 3 "title": "Buy groceries", 4 "completed": false 5 }, 6 { 7 "title": "Read book", 8 "completed": false 9 } 10]

To test this request in a terminal using curl, you can use the following command:

Bash
1curl -X POST http://localhost:8080/api/todos/bulk \ 2 -H "Content-Type: application/json" \ 3 -d '[ 4 {"title": "Buy groceries", "completed": false}, 5 {"title": "Read book", "completed": false} 6 ]'

This command sends a POST request to the specified endpoint with a JSON payload containing multiple todo items, effectively demonstrating the bulk uploading feature in action.

Summary and Next Steps

Throughout this lesson, you've progressively built and understood the bulk uploading feature for the ToDo app utilizing Gin. We’ve seen how to register routes, handle and validate incoming requests, manage data services, and ensure the integrity of our todos through duplicate checks.

Moving forward, as you practice and engage with the exercises, think about the practical applications of bulk data operations beyond our ToDo app scenario. Exploring these concepts enhances your ability to create more efficient and user-friendly applications.

You're well on your way to making a highly functional ToDo app! Keep up the great work as you tackle the practice problems and further refine these new skills.

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.