How to create a todo app in Golang with React and MongoDB

How to create a todo app in Golang with React and MongoDB

A complete step by step tutorial on how to create a todo app in Golang with React and MongoDB

The main purpose of this tutorial to get hands-on experience in Golang. When I was learning Golang, I realized there are very few tutorials and articles out there which give you a complete end to end hands-on experience.This is the GitHub link for the complete code.In this tutorial, we will build a todo app in which the server will be in Golang, the database will be MongoDB, and the frontend will be in React.

  1. Server — Go
  2. Database — MongoDB
  3. Frontend — React

I am assuming that you have Go installed and have a basic understanding of it. If you don’t, I’ll explain all the steps and will mention the references of the related topics.Create a project directory and give it an appropriate name.I am using go-todo.

Let’s first create the server.

Server in Golang

Create a serverdirectory inside the go-todo.The serverdirectory structure will be:

go-todo
  - server
    - middleware
      - middleware.go
    - models 
      - models.go
    - router 
      - router.go
    - main.go

In the server, we require 2 dependencies: the first to connect with MongoDB and the second to create RESTAPIs.

We are going to use the official MongoDB Go Driver from MongoDB.To install it run the below command in the terminal or command window.

go get go.mongodb.org/mongo-driver

Second, install the gorilla/muxpackage for the router. muxis one of the most popular packages for the router in the Golang.To install it run the below command in the terminal or command window.

go get -u github.com/gorilla/mux

Models

Once both the packages installed successfully, create a modelsdirectory and models.gofile inside it and paste the below code.

package modelsimport "go.mongodb.org/mongo-driver/bson/primitive"type ToDoList struct {

ID primitive.ObjectID json:"_id,omitempty" bson:"_id,omitempty" Task string json:"task,omitempty" Status bool json:"status,omitempty" }

The first line is the package name for this file. To learn more about packages follow this link.The second line is importprimitive from mongo-driverthe package.To define how the data will get stored in the database we have to create a modelfor it. In Golang, we use structtype for this.

In the ToDoListwe have 3 fields:

  1. ID: This objectID will be generated by the MongoDB
  2. Task: The test
  3. Status: true or false

The type of id in MongoDB is Object(id)

Note: ToDoList must be in uppercase as it is exported.

Middleware

Create a new folder by name middleware in the server directory and create a new file middleware.go inside it and paste the below code in it.

package middleware

import ( "context" "encoding/json" "fmt" "log" "net/http"

"../models"
"github.com/gorilla/mux"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"

)

// DB connection string // for localhost mongoDB // const connectionString = "mongodb://localhost:27017" const connectionString = "Connection String"

// Database Name const dbName = "test"

// Collection name const collName = "todolist"

// collection object/instance var collection *mongo.Collection

// create connection with mongo db func init() {

// Set client options
clientOptions := options.Client().ApplyURI(connectionString)

// connect to MongoDB
client, err := mongo.Connect(context.TODO(), clientOptions)

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

// Check the connection
err = client.Ping(context.TODO(), nil)

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

fmt.Println("Connected to MongoDB!")

collection = client.Database(dbName).Collection(collName)

fmt.Println("Collection instance created!")

}

// GetAllTask get all the task route func GetAllTask(w http.ResponseWriter, r http.Request) { w.Header().Set("Context-Type", "application/x-www-form-urlencoded") w.Header().Set("Access-Control-Allow-Origin", "") payload := getAllTask() json.NewEncoder(w).Encode(payload) }

// CreateTask create task route func CreateTask(w http.ResponseWriter, r http.Request) { w.Header().Set("Context-Type", "application/x-www-form-urlencoded") w.Header().Set("Access-Control-Allow-Origin", "") w.Header().Set("Access-Control-Allow-Methods", "POST") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") var task models.ToDoList _ = json.NewDecoder(r.Body).Decode(&task) // fmt.Println(task, r.Body) insertOneTask(task) json.NewEncoder(w).Encode(task) }

// TaskComplete update task route func TaskComplete(w http.ResponseWriter, r *http.Request) {

w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "PUT")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

params := mux.Vars(r)
taskComplete(params["id"])
json.NewEncoder(w).Encode(params["id"])

}

// UndoTask undo the complete task route func UndoTask(w http.ResponseWriter, r *http.Request) {

w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "PUT")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

params := mux.Vars(r)
undoTask(params["id"])
json.NewEncoder(w).Encode(params["id"])

}

// DeleteTask delete one task route func DeleteTask(w http.ResponseWriter, r http.Request) { w.Header().Set("Context-Type", "application/x-www-form-urlencoded") w.Header().Set("Access-Control-Allow-Origin", "") w.Header().Set("Access-Control-Allow-Methods", "DELETE") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") params := mux.Vars(r) deleteOneTask(params["id"]) json.NewEncoder(w).Encode(params["id"]) // json.NewEncoder(w).Encode("Task not found")

}

// DeleteAllTask delete all tasks route func DeleteAllTask(w http.ResponseWriter, r http.Request) { w.Header().Set("Context-Type", "application/x-www-form-urlencoded") w.Header().Set("Access-Control-Allow-Origin", "") count := deleteAllTask() json.NewEncoder(w).Encode(count) // json.NewEncoder(w).Encode("Task not found")

}

// get all task from the DB and return it func getAllTask() []primitive.M { cur, err := collection.Find(context.Background(), bson.D{{}}) if err != nil { log.Fatal(err) }

var results []primitive.M
for cur.Next(context.Background()) {
    var result bson.M
    e := cur.Decode(&result)
    if e != nil {
        log.Fatal(e)
    }
    // fmt.Println("cur..>", cur, "result", reflect.TypeOf(result), reflect.TypeOf(result["_id"]))
    results = append(results, result)

}

if err := cur.Err(); err != nil {
    log.Fatal(err)
}

cur.Close(context.Background())
return results

}

// Insert one task in the DB func insertOneTask(task models.ToDoList) { insertResult, err := collection.InsertOne(context.Background(), task)

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

fmt.Println("Inserted a Single Record ", insertResult.InsertedID)

}

// task complete method, update task's status to true func taskComplete(task string) { fmt.Println(task) id, _ := primitive.ObjectIDFromHex(task) filter := bson.M{"_id": id} update := bson.M{"$set": bson.M{"status": true}} result, err := collection.UpdateOne(context.Background(), filter, update) if err != nil { log.Fatal(err) }

fmt.Println("modified count: ", result.ModifiedCount)

}

// task undo method, update task's status to false func undoTask(task string) { fmt.Println(task) id, _ := primitive.ObjectIDFromHex(task) filter := bson.M{"_id": id} update := bson.M{"$set": bson.M{"status": false}} result, err := collection.UpdateOne(context.Background(), filter, update) if err != nil { log.Fatal(err) }

fmt.Println("modified count: ", result.ModifiedCount)

}

// delete one task from the DB, delete by ID func deleteOneTask(task string) { fmt.Println(task) id, _ := primitive.ObjectIDFromHex(task) filter := bson.M{"_id": id} d, err := collection.DeleteOne(context.Background(), filter) if err != nil { log.Fatal(err) }

fmt.Println("Deleted Document", d.DeletedCount)

}

// delete all the tasks from the DB func deleteAllTask() int64 { d, err := collection.DeleteMany(context.Background(), bson.D{{}}, nil) if err != nil { log.Fatal(err) }

fmt.Println("Deleted Document", d.DeletedCount)
return d.DeletedCount

}

MongoDB Set up

First set up the MongoDB connection.Here I am using MongoDB Atlas for the demo. You can sign up for free tier, it gives you 512MB of storage, that is more than enough for learning purpose.

Sign up for MongoDB Atlas. Follow the link instructions.Once you have your cluster ready, a few things need to be done.First, white list your IP address.

  • Click on “Network Access” under “Security”.
  • Click “ADD IP ADDRESS” and select “ADD CURRENT IP ADDRESS”. This will allow only your computer to interact with it.

Whitelist IP address

Second, create a user. You can learn more about the user in this link

  • Click “Database Access” under “Security” and create a new user.
  • I have admin as a user.

atlas user

Now, its time to get the connection string.

  • Go to “Cluster” and click “connect” and then “Connect Your Application”.
  • Copy the connection string and paste it in connectionString in middleware.go

connection string

Let me explain the functionality. All the functions which are in uppercase are exported and will be used in router.go which we will be writing in some time.

  • init(): runs only once throughout the program life. In the init function the connection to the MongoDB will be established. Check out this answer on StackOverflow to learn more about init
  • GetAllTask: First it set the header to tackle the cors issue and then it will call the getAllTask() function. It uses a bson package to get the data from the MongoDB. bson.M is used where M is an unordered, concise representation of a BSON document. It should generally be used to Serialize BSON. All the documents returned in primitive.M type. Package primitive contains types similar to Go primitives for BSON types can do not have direct Go primitive representations
  • CreateTask: It first decodes the request body and store in models.ToDoList type. It is imported from a models package. Then, it will call insertOneTask function and insert the task into the collection
  • TaskComplete: It is an update request where it will update the task’s status according to task ID. To get the params from the URL, we are using mux package. Using mux , send task id as a string to the taskComplete function. MongoDB assigns ids to the data in ObjectID format. To get the ObjectID from the task id (string), we are using primitive package’s method ObjectIDFromHex. It will return the ObjectID. The updateOne method requires 3 arguments context, filter and update. context is Background you can learn more about context package from this link . The second argument is filter , filter by id and the third one is update where status is updated to true
  • .UndoTask: This is same as TaskComplete, it only updates the task’s status to false
  • DeleteTask: It is a delete request. First, it’ll get the task id from the URL and then pass it to the deleteOneTask . It will retrieve the ObjectIDof the task and then it will delete the task by its id from the collection
  • DeleteAllTask: As its names speak for itself, it deletes all the tasks from the collection.

The middleware is complete.RouterCreate a router folder in the server directory and then create a new file router.go in it. Paste the below code in the file.

package router

import ( "../middleware" "github.com/gorilla/mux" )

// Router is exported and used in main.go func Router() *mux.Router {

router := mux.NewRouter()

router.HandleFunc("/api/task", middleware.GetAllTask).Methods("GET", "OPTIONS")
router.HandleFunc("/api/task", middleware.CreateTask).Methods("POST", "OPTIONS")
router.HandleFunc("/api/task/{id}", middleware.TaskComplete).Methods("PUT", "OPTIONS")
router.HandleFunc("/api/undoTask/{id}", middleware.UndoTask).Methods("PUT", "OPTIONS")
router.HandleFunc("/api/deleteTask/{id}", middleware.DeleteTask).Methods("DELETE", "OPTIONS")
router.HandleFunc("/api/deleteAllTask", middleware.DeleteAllTask).Methods("DELETE", "OPTIONS")
return router

}

First, we’re importing all task functions from the middleware package.Second, we’re using mux package to create routes.

  • Line 11: create a new instance of the router using mux.NewRouter()
  • Line 13: GET method to get all task from the DB. In Methods the first parameter is Method in this case, it is GET and second OPTIONS, this is to tackle cors
  • .Line 14: POST method to create a task in the DB.
  • Line 15: PUT method to update the task’s status to true in the DB. The task’s id is passed as params in the URL.
  • Line 16: PUT method to update the task’s status to false in the DB. The task’s id is passed as params in the URL.
  • Line 17: DELETE method to delete the task from the DB. The task’s id is passed as params in the URL.
  • Line 18: DELETE method to delete all the tasks from the DB.
  • Line 19: Return the router instance. This router will be served in the main.go

main.go

Create a main.go file in the server directory. Paste the below code in it.

package mainimport (
    "fmt"
    "log"
    "net/http"
    "./router"
)func main() {    r := router.Router()    fmt.Println("Starting server on the port 8080...")    log.Fatal(http.ListenAndServe(":8080", r))
}

Import net/http package to serve the routes at 8080 port and ./router to import router package.Create an instance of router package.

r := router.Router()

Serve/host the application on the 8080 port.

http.ListenAndServe(":8080", r)

A log package is used to track logs.Open the terminal from the server directory and run the below command to serve the server. You’ll see the output as the image.

go run main.go

serve

You can test the APIs using POSTMAN

Frontend in React

We’re using the create-react-app module as a boilerplate for this project. You can learn more about it here.Run the below command from the project directory go-todo

npx create-react-app client

It will take a while to install. Once it is finished, install the following dependencies for the project. Open terminal from the client directory.

  1. Axios: For interacting with the server rest APIs.
  2. semantic-ui-react: It is a great react component library to quickly build a frontend. Refer to this link to learn more it.
npm install axiosnpm install semantic-ui-react

Semantic UI React provides React components while Semantic UI provides themes as CSS stylesheets. Add the stylesheet in index.html inside the public folder. Check the semantic UI version from this link.

<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css"/>

Update the title in the title tag. This will be the title of the tab in the browser. Save the index.html

<title>ToDo App</title>

Go to the src folder and open App.js. Delete the content in it and paste the below code.

import React from "react";
import "./App.css";// import the Container Component from the semantic-ui-react
import { Container } from "semantic-ui-react";// import the ToDoList component
import ToDoList from "./To-Do-List";function App() {
  return (
    <div>
      <Container>
        <ToDoList />
      </Container>
    </div>
  );
}
export default App;

Create a new file To-Do-List.js inside the src folder. This component will be the frontend.You can download the component from the GitHub.You can save the file and it is ready to go.

Run the Application

  • Open the terminal and start the server from the server directory.
go run main.go
  • Open the terminal and start the react application from the client directory.
npm start

index

  • Create tasks

Create Task

  • Complete task by clicking Done

Task Complete

  • Undo a task

Undo Task

  • Delete Task

Delete Task

Congratulations! You just build a To-Do App in Golang.For the complete code. This is the Github Link.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading about React, Redux and Django

React - The Complete Guide (incl Hooks, React Router, Redux)

Modern React with Redux [2019 Update]

Best 50 React Interview Questions for Frontend Developers in 2019

JavaScript Basics Before You Learn React

Microfrontends — Connecting JavaScript frameworks together (React, Angular, Vue etc)

Creating RESTful APIs with NodeJS and MongoDB Tutorial

Building REST API with Nodejs / MongoDB /Passport /JWT

Building a full CRUD Angular 8 Universal and MongoDB SSR

Node.js With MongoDB Authentication

Learn Database Management System - Database modeling with Golang & PostgreSQL

A guide to Face Detection with Golang and OpenCV

Go Programming Language Tutorial | Golang Tutorial For Beginners | Go / Golang Crash Course

Google’s Go Essentials For Node.js / JavaScript Developers




reactjs mongodb go

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

What's new in the go 1.15

Go announced Go 1.15 version on 11 Aug 2020. Highlighted updates and features include Substantial improvements to the Go linker, Improved allocation for small objects at high core counts, X.509 CommonName deprecation, GOPROXY supports skipping proxies that return errors, New embedded tzdata package, Several Core Library improvements and more.

MongoDB Database and java applications

Enroll for free demo to acquire the best knowledge on the schema-less database from live industry experts through MongoDB training

Using Go with MongoDB with the Go Driver MongoDB

Like other official MongoDB drivers, the Go driver is an integral part of the Go programming language and provides a convenient opportunity to use MongoDB as a solution for the Go program databases.

Which is the Best MongoDB GUI?

Our MongoDB Online Training provide you to learn about MongoDB strategies with realty. Our MongoDB Online Training also includes live sessions, live Projects, and much

Improve the MongoDB Aggregation Framework

Our MongoDB Online Training provide you to learn about MongoDB strategies with realty. Our MongoDB Online Training also includes live sessions, live Projects, and much