Golang for Web (Part-II): Gofiber REST API + Mongo DB Atlas
Tags: go, gofiber, webdev, mongodb
Published: 2021-03-21 07:50:47

Here is our second part of the **Golang for Web** series. In this article, we will see how we can add database to our app to store our todos.
Before that if you didn't follow the first part of this series, check that [here](https://dev.to/devsmranjan/golang-build-your-first-rest-api-with-fiber-24eh).

If you've done that, let's dive into our code.

## What will we build? 🤔

We will continue our Todo application from our first part of this series. Here we will add `Mongo DB` as our database to store our todos.

## Prerequisites 📝

-   A basic knowledge of Golang syntax.
-   Go version 1.14 or above installed on your machine. [Install from here](https://golang.org/)
-   Postman or any other related app installed on your machine. [Download Postman](https://www.postman.com/downloads/)
-   Part-I of this project. Clone the project from [here](https://github.com/devsmranjan/golang-fiber-basic-todo-app)

If you have these, Let's get started 🚀

![Let's Begin](https://dev-to-uploads.s3.amazonaws.com/i/jsnlc5276a63380551pz.gif)

## Let’s Begin 🏁

### 1. Setup our Project

First, Let's open our previous project in our VS Code (or any other Code Editor/ IDE).

Now, run the below command to install all the dependencies.

```shell
go get
```

Let's install `mongo-driver` for `Mongo DB` and `godotenv` to manage environment variables.	

```shell
go get -u go.mongodb.org/mongo-driver github.com/joho/godotenv
```

Now, create `models`, `config` directories, and `.env` file in our root directory.

Our root directory will look like this:

```shell
.
|____config
|____controllers
| |____todo.go
|____models
|____routes
| |____todo.go
|____.env
|____go.mod
|____go.sum
|____main.go
```

### 2. Setup Mongo DB

Let's create an account at https://www.mongodb.com/ or you can sign in to your existing account.

Now create a new project, named `gofiber-todo-api` (or anything you want) and build a cluster.

Wait some time, while mongo db is creating a cluster for you.

![Waiting](https://dev-to-uploads.s3.amazonaws.com/i/friu8n2uqibchbcz8wnp.gif)

Now click on **connect**
Then, add a database user and choose **Allow Acess from Anywhere** under **Add a connection IP address** and click on **Add IP Address**
![Add IP Address](https://dev-to-uploads.s3.amazonaws.com/i/y5bopjo8kq3ndmfdbo4r.png)

Then choose **Connect your application** under **connection method**. Choose **Go** from the driver dropdown and **1.4 or later** from the version dropdown.

![Connection](https://dev-to-uploads.s3.amazonaws.com/i/yadm3uzmhn5z9k6eaflx.png)

Now copy that connection string. and paste it in `.env` file like below.

```
MONGO_URI=mongodb+srv://<dbUser>:<password>@cluster0.oynlp.mongodb.net/<dbname>?retryWrites=true&w=majority
```

Here replace `<dbUser>` with your database username, `<password>` with your user password, and `<dbname>` with a database name.

Additionally, add another two variables in `.env` file as below.

```
DATABASE_NAME=<dbname>
TODO_COLLECTION=todos
```

Here `DATABASE_NAME` is same as `<dbname>` in the `MONGO_URI`.


### 3. Configure Mongo DB

Let's create a `db.go` file inside `config` directory.

Now declare the package and import our required imports as below.

`config/db.go` will look like :

```go
package config

import (
	"context"
	"log"
	"os"
	"time"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readpref"
)
```

Then add `MongoInstance` as below.

```go
// MongoInstance : MongoInstance Struct
type MongoInstance struct {
	Client *mongo.Client
	DB     *mongo.Database
}

// MI : An instance of MongoInstance Struct
var MI MongoInstance
```

`config/db.go` will look like :

```go
package config

import (
    "context"
    "fmt"
	"log"
	"os"
	"time"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readpref"
)

// MongoInstance : MongoInstance Struct
type MongoInstance struct {
	Client *mongo.Client
	DB     *mongo.Database
}

// MI : An instance of MongoInstance Struct
var MI MongoInstance
```

Now let's create a `ConnectDB()` method with all the connection configuration.

```go
// ConnectDB - database connection
func ConnectDB() {
	client, err := mongo.NewClient(options.Client().ApplyURI(os.Getenv("MONGO_URI")))
	if err != nil {
		log.Fatal(err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	err = client.Connect(ctx)
	if err != nil {
		log.Fatal(err)
	}

	err = client.Ping(ctx, readpref.Primary())
	if err != nil {
		log.Fatal(err)
    }

    fmt.Println("Database connected!")

	MI = MongoInstance{
		Client: client,
		DB:     client.Database(os.Getenv("DATABASE_NAME")),
	}
}

```

Let's go to `main.go` and import the following packages

```golang
import (
	"log" // new

	"github.com/devsmranjan/golang-fiber-basic-todo-app/config" // new
	"github.com/devsmranjan/golang-fiber-basic-todo-app/routes"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/joho/godotenv" // new
)
```

Inside the `main()` initiate `dotenv` like below.

```go
// dotenv
err := godotenv.Load()
if err != nil {
	log.Fatal("Error loading .env file")
}
```

and call the db configuration method `ConnectDB()`

```go
// config db
config.ConnectDB()
```

Now our `main()` will look like :

```go
func main() {
	app := fiber.New()
	app.Use(logger.New())

	// dotenv
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading .env file")
	}

	// config db
	config.ConnectDB()

	// setup routes
	setupRoutes(app)

	// Listen on server 8000 and catch error if any
	err = app.Listen(":8000")

	// handle error
	if err != nil {
		panic(err)
	}
}
```

Now let's run our app

```shell
go run main.go
```

You will get output like below

```shell
Database connected!

 ┌───────────────────────────────────────────────────┐
 │                    Fiber v2.2.0                   │
 │               http://127.0.0.1:8000               │
 │                                                   │
 │ Handlers ............ 12  Threads ............. 4 │
 │ Prefork ....... Disabled  PID ............. 55680 │
 └───────────────────────────────────────────────────┘

```

It means our database successfully connected.

Additional: To reload the server automatically, you can install [air](https://github.com/cosmtrek/air) globally in your machine.

Now to run our app, in your root directory run

```shell
air
```

or for debug mode, you can run

```shell
air -d
```

### 4. Create Model

Now create a file named `todo.go` inside `models` directory.
And create our `Todo` like below

```go
package models

import (
	"time"
)

// Todo - todo model
type Todo struct {
	ID        *string   `json:"id,omitempty" bson:"_id,omitempty"`
	Title     *string   `json:"title"`
	Completed *bool     `json:"completed"`
	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
}

```

### 5. Connect Database with Controllers

Now go to `controllers/todo.go` and remove the following code first.

```diff
- // Todo : todo model
- type Todo struct {
- 	ID        int    `json:"id"`
- 	Title     string `json:"title"`
-	Completed bool   `json:"completed"`
- }
-
- var todos = []*Todo{
-	{
-		ID:        1,
-		Title:     "Walk the dog 🦮",
-		Completed: false,
-	},
-	{
-		ID:        2,
-		Title:     "Walk the cat 🐈",
-		Completed: false,
-	},
- }
```

And let's import required packages

```go
import (
	"os" // new
	"strconv"

	"github.com/devsmranjan/golang-fiber-basic-todo-app/config" // new
	"github.com/devsmranjan/golang-fiber-basic-todo-app/models" // new
	"github.com/gofiber/fiber/v2"
	"go.mongodb.org/mongo-driver/bson" // new
	"go.mongodb.org/mongo-driver/bson/primitive" // new
	"go.mongodb.org/mongo-driver/mongo" // new
)

```

#### **5.1 - GetTodos()**

```diff
+   todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
+
+	// Query to filter
+	query := bson.D{{}}
+
+	cursor, err := todoCollection.Find(c.Context(), query)
+
+	if err != nil {
+		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+			"success": false,
+			"message": "Something went wrong",
+			"error":   err.Error(),
+		})
+	}
+
+	var todos []models.Todo = make([]models.Todo, 0)
+
+	// iterate the cursor and decode each item into a Todo
+	err = cursor.All(c.Context(), &todos)
+	if err != nil {
+		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+			"success": false,
+			"message": "Something went wrong",
+			"error":   err.Error(),
+		})
+	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"data": fiber.Map{
			"todos": todos,
		},
	})
```

Now the `GetTodos()` will look like:

```go
// GetTodos : get all todos
func GetTodos(c *fiber.Ctx) error {
    todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))

	// Query to filter
	query := bson.D{{}}

	cursor, err := todoCollection.Find(c.Context(), query)

	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"message": "Something went wrong",
			"error":   err.Error(),
		})
	}

	var todos []models.Todo = make([]models.Todo, 0)

	// iterate the cursor and decode each item into a Todo
	err = cursor.All(c.Context(), &todos)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"message": "Something went wrong",
			"error":   err.Error(),
		})
	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"data": fiber.Map{
			"todos": todos,
		},
	})
}

```

#### **5.2 - CreateTodo()**

```diff
+   todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
+
-	type Request struct {
-		Title string `json:"title"`
-	}
-
-	var data Request
+
+   data := new(models.Todo)

	err := c.BodyParser(&body)

	// if error
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"error":   err,
		})
	}
-
-	// create a todo variable
-	todo := &Todo{
-		ID:        len(todos) + 1,
-		Title:     body.Title,
-		Completed: false,
-	}
-
-	// append in todos
-	todos = append(todos, todo)
-
+   data.ID = nil
+	f := false
+	data.Completed = &f
+	data.CreatedAt = time.Now()
+	data.UpdatedAt = time.Now()
+
+	result, err := todoCollection.InsertOne(c.Context(), data)
+
+	if err != nil {
+		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+			"success": false,
+			"message": "Cannot insert todo",
+			"error":   err,
+		})
+	}
+
+	// get the inserted data
+	todo := &models.Todo{}
+	query := bson.D{{Key: "_id", Value: result.InsertedID}}
+
+	todoCollection.FindOne(c.Context(), query).Decode(todo)

	return c.Status(fiber.StatusCreated).JSON(fiber.Map{
		"success": true,
		"data": fiber.Map{
			"todo": todo,
		},
	})

```

Now `CreateTodo()` will look like:

```go
// CreateTodo : Create a todo
func CreateTodo(c *fiber.Ctx) error {
    todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))

	data := new(models.Todo)

	err := c.BodyParser(&data)

	// if error
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"error":   err,
		})
	}

	data.ID = nil
	f := false
	data.Completed = &f
	data.CreatedAt = time.Now()
	data.UpdatedAt = time.Now()

	result, err := todoCollection.InsertOne(c.Context(), data)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot insert todo",
			"error":   err,
		})
	}

	// get the inserted data
	todo := &models.Todo{}
	query := bson.D{{Key: "_id", Value: result.InsertedID}}

	todoCollection.FindOne(c.Context(), query).Decode(todo)

	return c.Status(fiber.StatusCreated).JSON(fiber.Map{
		"success": true,
		"data": fiber.Map{
			"todo": todo,
		},
	})
}
```

#### **5.3 - GetTodo()**

```diff
+   todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))

	// get parameter value
	paramID := c.Params("id")

	// convert parameter value string to int
-	id, err := strconv.Atoi(paramID)
+   id, err := primitive.ObjectIDFromHex(paramID)

	// if error in parsing string to int
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse Id",
			"error":   err,
		})
	}

	// find todo and return
-	for _, todo := range todos {
-		if todo.ID == id {
-			return c.Status(fiber.StatusOK).JSON(fiber.Map{
-				"success": true,
-				"data": fiber.Map{
-					"todo": todo,
-				},
-			})
-		}
-	}
-
-	// if todo not available
-	return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
-		"success": false,
-		"message": "Todo not found",
-	})
+
+   todo := &models.Todo{}
+
+	query := bson.D{{Key: "_id", Value: id}}
+
+	err = todoCollection.FindOne(c.Context(), query).Decode(todo)
+
+	if err != nil {
+		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
+			"success": false,
+			"message": "Todo Not found",
+			"error":   err,
+		})
+	}
+
+	return c.Status(fiber.StatusOK).JSON(fiber.Map{
+		"success": true,
+		"data": fiber.Map{
+			"todo": todo,
+		},
+	})
```

Now `GetTodo()` will look like:

```go
// GetTodo : get a single todo
// PARAM: id
func GetTodo(c *fiber.Ctx) error {
    todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))

	// get parameter value
	paramID := c.Params("id")

    // convert parameterID to objectId
	id, err := primitive.ObjectIDFromHex(paramID)

	// if error while parsing paramID
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse Id",
			"error":   err,
		})
	}

	// find todo and return

	todo := &models.Todo{}

	query := bson.D{{Key: "_id", Value: id}}

	err = todoCollection.FindOne(c.Context(), query).Decode(todo)

	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"success": false,
			"message": "Todo Not found",
			"error":   err,
		})
	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"data": fiber.Map{
			"todo": todo,
		},
	})
}
```

#### **5.4 - UpdateTodo()**

```diff
+   todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))

	// find parameter
	paramID := c.Params("id")

	// convert parameter string to int
-	id, err := strconv.Atoi(paramID)

	// if parameter cannot parse
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse id",
			"error":   err,
		})
	}
-
-	// request structure
-	type Request struct {
-		Title     *string `json:"title"`
-		Completed *bool   `json:"completed"`
-	}

	var data Request
	err = c.BodyParser(&data)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"error":   err,
		})
	}
-
-	var todo *Todo
-
-	for _, t := range todos {
-		if t.ID == id {
-			todo = t
-			break
-		}
-	}
-
-	if todo.ID == 0 {
-		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
-			"success": false,
-			"message": "Not found",
-		})
-	}
+
+	query := bson.D{{Key: "_id", Value: id}}
+
+	// updateData
+	var dataToUpdate bson.D

	if data.Title != nil {
-		todo.Title = *data.Title
+       dataToUpdate = append(dataToUpdate, bson.E{Key: "title", Value: data.Title})
	}

	if data.Completed != nil {
-		todo.Completed = *data.Completed
+       dataToUpdate = append(dataToUpdate, bson.E{Key: "completed", Value: data.Completed})
	}
+
+    dataToUpdate = append(dataToUpdate, bson.E{Key: "updatedAt", Value: time.Now()})
+
+	update := bson.D{
+		{Key: "$set", Value: dataToUpdate},
+	}
+
+	// update
+	err = todoCollection.FindOneAndUpdate(c.Context(), query, update).Err()
+
+	if err != nil {
+		if err == mongo.ErrNoDocuments {
+			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
+				"success": false,
+				"message": "Todo Not found",
+				"error":   err,
+			})
+		}
+
+		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+			"success": false,
+			"message": "Cannot update todo",
+			"error":   err,
+		})
+	}
+
+	// get updated data
+	todo := &models.Todo{}
+
+	todoCollection.FindOne(c.Context(), query).Decode(todo)

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"data": fiber.Map{
			"todo": todo,
		},
	})

```

Now `UpdateTodo()` will look like:

```go
// UpdateTodo : Update a todo
// PARAM: id
func UpdateTodo(c *fiber.Ctx) error {
    todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))

	// find parameter
	paramID := c.Params("id")

	// convert parameterID to objectId
	id, err := primitive.ObjectIDFromHex(paramID)

	// if parameter cannot parse
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse id",
			"error":   err,
		})
	}

	// var data Request
	data := new(models.Todo)
	err = c.BodyParser(&data)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"error":   err,
		})
	}

	query := bson.D{{Key: "_id", Value: id}}

	// updateData
	var dataToUpdate bson.D

	if data.Title != nil {
		// todo.Title = *data.Title
		dataToUpdate = append(dataToUpdate, bson.E{Key: "title", Value: data.Title})
	}

	if data.Completed != nil {
		// todo.Completed = *data.Completed
		dataToUpdate = append(dataToUpdate, bson.E{Key: "completed", Value: data.Completed})
	}

	dataToUpdate = append(dataToUpdate, bson.E{Key: "updatedAt", Value: time.Now()})

	update := bson.D{
		{Key: "$set", Value: dataToUpdate},
	}

	// update
	err = todoCollection.FindOneAndUpdate(c.Context(), query, update).Err()

	if err != nil {
		if err == mongo.ErrNoDocuments {
			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
				"success": false,
				"message": "Todo Not found",
				"error":   err,
			})
		}

		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot update todo",
			"error":   err,
		})
	}

	// get updated data
	todo := &models.Todo{}

	todoCollection.FindOne(c.Context(), query).Decode(todo)

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"data": fiber.Map{
			"todo": todo,
		},
	})
}
```

#### **5.5 - DeleteTodo()**

```diff
+   todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))

	// get param
	paramID := c.Params("id")
-
-	// convert param string to int
+	// convert parameter to object id
-	id, err := strconv.Atoi(paramID)
+	id, err := primitive.ObjectIDFromHex(paramID)

	// if parameter cannot parse
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse id",
			"error":   err,
		})
	}

	// find and delete todo
-	for i, todo := range todos {
-		if todo.ID == id {
-
-			todos = append(todos[:i], todos[i+1:]...)
-
-			return c.SendStatus(fiber.StatusNoContent)
-		}
-	}
+
+	query := bson.D{{Key: "_id", Value: id}}
+
+	err = todoCollection.FindOneAndDelete(c.Context(), query).Err()
+
+	if err != nil {
+		if err == mongo.ErrNoDocuments {
+			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
+				"success": false,
+				"message": "Todo Not found",
+				"error":   err,
+			})
+		}
+
+		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+			"success": false,
+			"message": "Cannot delete todo",
+			"error":   err,
+		})
+	}
-
-	// if todo not found
-	return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
-		"success": false,
-		"message": "Todo not found",
-	})
+
+    return c.SendStatus(fiber.StatusNoContent)
```

Now `DeleteTodo()` will look like:

```go
// DeleteTodo : Delete a todo
// PARAM: id
func DeleteTodo(c *fiber.Ctx) error {
	todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))

	// get param
	paramID := c.Params("id")

	// convert parameter to object id
	id, err := primitive.ObjectIDFromHex(paramID)

	// if parameter cannot parse
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse id",
			"error":   err,
		})
	}

	// find and delete todo
	query := bson.D{{Key: "_id", Value: id}}

	err = todoCollection.FindOneAndDelete(c.Context(), query).Err()

	if err != nil {
		if err == mongo.ErrNoDocuments {
			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
				"success": false,
				"message": "Todo Not found",
				"error":   err,
			})
		}

		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot delete todo",
			"error":   err,
		})
	}

	return c.SendStatus(fiber.StatusNoContent)
}
```

Awesome !!! We did great so far. 🤝

### 6. Test our endpoints 🧪

Let's test our API in Postman. Before that don't forget to run the server.

To get all Todos, give a `GET` request to `localhost:8000/api/todos`

![Get all todos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5khfgg6y0ixe684md2d0.png)

To create a new todo, give a `POST` request to `localhost:8000/api/todos` with a `title: <String>` in the request body.

![New Todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5oz7gde5wscnkfgwqv6v.png)

Now, you can add more todos like:
- Play Cricket 🏏
- Watch WandaVision 🍿

Let's get all our todos again.
![All todos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yf6czap8o23rtmi32kky.png)

Yaaahooooo! Isn't this amazing? 🤩

![Yes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y9y183dxii8l8p4lpisi.gif)

Now, to get todo by id, give a `GET` request to `localhost:8000/api/todos/:id`
Here replace `:id` with a todo `id`.

![Get single todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oyunwo3cr90xw5c3l339.png)

Now, let's update a todo by giving a `PUT` request to `localhost:8000/api/todos/:id` with a `title: <String>` or `completed: <Boolean>` or both in the request body.
Here replace `:id` with a todo `id`.

![Update todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vkaolqp4p2clwik6gmgb.png)

To delete a todo, give a `DELETE` request to `localhost:8000/api/todos/:id`
Here replace `:id` with a todo `id`.

![Delete Todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/blhv0smc6vrewxr2gy4b.png)

Congratulations 🥳 🥳 🥳 We did it 💪🏻

![Woohoo!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8k10qrvkjxdtgy0ini7v.gif)

## Conclusion 📋

For more information about gofiber, I suggest taking a deeper look at the documentation here https://docs.gofiber.io/

Mongo DB official [documentation](https://www.mongodb.com/blog/post/quick-start-golang--mongodb--how-to-read-documents).

Here is my GitHub link to this project - https://github.com/devsmranjan/golang-fiber-basic-todo-app/

Thank you for reading my article 🙂 . I hope you have learned something here.

If you want to improve this project, fork the repo [here](https://github.com/devsmranjan/golang-fiber-basic-todo-app).

Happy coding 👨‍💻👩‍💻 and stay tuned for my next post in this series!

Thanks! Don't forget to give a ♥️ and follow :)