This content originally appeared on HackerNoon and was authored by Ilia Ivankin
Many have wondered how a simple task sheet or applications that provide such functionality work. In this article, I invite you to consider how you can write your small service in Go in a couple of hours and put everything in a database.
Why Goalng?
The Keys are Simplicity and Efficiency.
- Minimalistic Design: Easy to learn and use, focusing on simplicity.
\
- Fast Compilation: Compiled to native code, ensuring speedy execution and deployment.
\
- Concurrency Goroutines: Lightweight threads managed by Go runtime make concurrent programming more superficial and efficient. Channels: Facilitate safe communication between goroutines, avoiding the pitfalls of shared memory. Robust Standard Library.
\
- Rich Ecosystem: Extensive standard library covering web servers, cryptography, I/O, and more.
\
- Cross-Platform: Compiles to various operating systems and architectures without modification.
Let's Check the Trends:
The Rise of Cloud-Native Development and Microservices
- ==Docker== allows developers to package applications into containers, ensuring consistency across different environments. For example, a web server can be run in a Docker container to ensure it behaves similarly in development and production. Consul Consul provides service discovery and configuration management.
\
- ==K8s== (Kubernetes) - automates the deployment, scaling, and management of containerized applications.
\
- ==gRPC== is a high-performance, open-source universal RPC framework. Example: Using gRPC to enable efficient communication between microservices written in different languages.
\
- ==Terraform== is used to safely and efficiently build, change, and version infrastructure. An example is using Terraform scripts to provision cloud infrastructure on AWS.
Golang and the Internet of Things (IoT)
- ==NATS== is a simple, high-performance, open-source messaging system for cloud-native applications, IoT messaging, and microservices architectures.
\
- ==InfluxData== is a platform for handling time series data, which is essential for IoT applications.
Growing Adoption of Machine Learning and Artificial Intelligence (AI)
- ==“TensorFlow Go”== provides machine learning tools that can be used with Go.
\
- ==Gorgonia== is a library that brings machine-learning capabilities to Go.
\
- ==GoLearn== is a machine-learning library for Go.
The Expanding Golang Ecosystem
- ==Gin== is a high-performance HTTP web framework written in Golang.
\
- ==Viper== is a complete configuration solution for Go applications. For example, It Manages application configuration using Viper to support formats like JSON, YAML, and TOML.
\
- ==Cobra== is a library that creates powerful modern CLI applications.
\
- ==GORM== is an ORM library for Golang.
\
- ==Protocol Buffers (Protobuf)== is a method developed by Google for serializing structured data.
Why MongoDB?
We need to collect data for our tasks and be flexible. We don't need to create a schema or relationship between something.
\ What can we have using it:
- Flexible Schema: MongoDB allows for schema-less design, making it easy to handle unstructured or semi-structured data.
\
- Scalability: It supports horizontal scaling, allowing you to distribute data across multiple servers.
\
Rich Query Language: MongoDB provides a powerful query language, including support for complex queries, indexing, and aggregation.
\ That’s nice for our example:
{
"_id": "66532b210d9944a92a88ef4b",
"title": "Go to the groceries",
"description": "Purchase milk, eggs, and bread",
"completed": false
}
\ A local run with docker:
version: '3.1'
services:
mongo:
image: mongo
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
\ Now, we have DB, but we need to work with it as well.
Compass
MongoDB Compass is a graphical user interface (GUI) for MongoDB designed to facilitate developers, database administrators, and data analysts' interactions with their MongoDB databases. It provides a user-friendly visual representation of the data and powerful tools for querying, managing, and optimizing databases.
\ ==Download it here: https://www.mongodb.com/products/tools/compass.==
\ Why MongoDB Compass is Easy to Use:
- Graphical Interface: The graphical interface reduces the learning curve associated with using MongoDB, making it accessible to users with varying technical expertise.
\
- Drag-and-Drop Functionality: Many features, such as query building and schema design, use drag-and-drop functionality, simplifying complex operations and making them more intuitive.
\
- Real-Time Feedback: Compass provides real-time feedback as you interact with your data, allowing you to see the results of your queries and modifications immediately.
\
Comprehensive Documentation and Support: MongoDB Compass is backed by extensive documentation and a supportive community. Users can easily find tutorials, guides, and forums to help them navigate any challenges.
\
Fast Installations Before We Start
Install VS code (It's free).
Visit https://code.visualstudio.com/.
\ Installing Go Visit golang.org to download the installer for your operating system.
\ Follow the installation instructions provided on the website.
\ Verify the installation by opening a terminal/command prompt and typing:
go version
\ And then add the Golang extension:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
\ And run it:
go run main.go
System Design (Small, But Still ;D)
The document should have:
Title
Description
Status
\ Our previous JSON file as a reference: JSON
{
"_id": "66532b210d9944a92a88ef4b",
"title": "Go to the groceries",
"description": "Purchase milk, eggs, and bread",
"completed": false
}
\ Next step: Create main methods such as CRUD.
\ Create -The Create operation involves adding new records to a database. This is the initial step in data management, where new data entries are inserted into the database.
\ Read - The Read operation retrieves data from the database. It allows users to fetch and display data without modifying it.
\ Update - The Update operation involves modifying existing records in the database. It changes the data within a record while maintaining its identity.
\ Delete—The Delete operation permanently removes records from a database. It is often accompanied by a confirmation step to prevent accidental deletions.
\ We are going to implement only the “CREATE” or add method because I’d like to share a good example. After that, you can implement others.
\ Project structure:
todo-list/
│
├── cmd/
│ └── main.go
├── pkg/
│ └── handler
│ └── add_task.go
│ └── http_handler.go
│ └── mapper
│ └── task.go
│ └── model
│ └── task.go
│ └── usecase
│ └── task
│ └── repository
│ └── add_task.go
│ └── mongo_repositiry.go
│ └── repository.go
│ └── service
│ └── add_task.go
│ └── service.go
└── go.mod
I want to use the way to separate all responsibilities by folders.
- Handler - HTTP layer
- Model - structures for data
- Use cases - business layers with service and repository.
\ Let's start with a data structure for our app:
package model
import "go.mongodb.org/mongo-driver/bson/primitive"
type Task struct {
ID string json:"id"
Title string json:"title"
Desciption string json:"description"
Completed bool json:"completed"
}
type MongoTask struct {
ID primitive.ObjectID json:"id" bson:"_id"
Title string json:"title"
Desciption string json:"description"
Completed bool json:"completed"
}
Task - for HTTP request, MongoTask - for MongoDb layer. Using two structures is easy because sometimes we don't need to send additional data to our users. For example, we might have a secret field, like a username, which we must hide. Now that we know CRUD, let's code it!
\ Repository layer:
type Repository interface {
AddTask(ctx context.Context, task model.MongoTask) error
}
\ Service layer:
type TodoService interface {
AddTask(ctx context.Context, task model.Task) error
}
\ Let's connect and inject dependencies:
// Initialize repository, service, and handler
todoRepo := repository.NewMongoRepository(client)
todoService := service.NewService(todoRepo)
todoHandler := handler.NewHandler(todoService)
\ Finally, context and connections:
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Set MongoDB client options
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017").SetAuth(options.Credential{
Username: "root",
Password: "example",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal(err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
log.Println("Connected to MongoDB!")
// Initialize repository, service, and handler
todoRepo := repository.NewMongoRepository(client)
todoService := service.NewService(todoRepo)
todoHandler := handler.NewHandler(todoService)
// Set up routes
http.HandleFunc("/api/v1/add", todoHandler.AddTask)
// Create a server
srv := &http.Server{
Addr: ":8080",
Handler: nil,
}
// .. todo
}
Demo
Now, we have everything, and we can start to analyze what happens when we call our service.
curl -X POST http://localhost:8080/add
#-H "Content-Type: application/json"
#-d '{
"id": 1,
"title": "Buy groceries",
"completed": false
#}'
POST http://localhost:8080/api/v1/add
Content-Type: application/json
{
"title": "Add description to the structure",
"description": "your desc here..."
}
\ We will process the request using the handler layer, decode it using JSON lib, and send the model to the service layer.
func (h *Handler) AddTask(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
var task model.Task
err := json.NewDecoder(r.Body).Decode(&task)
if err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
err = h.Service.AddTask(ctx, task)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
}
\ Next step, process it in the service layer: (just proxy and convert the model to DTO or Entity for MongoDb).
func (s *Service) AddTask(ctx context.Context, task model.Task) error {
return s.Repo.AddTask(ctx, mapper.MapToDto(task))
}
\ Lastly, use the MongoDB client, and save the task to DB.
func (r *MongoRepository) AddTask(ctx context.Context, task model.MongoTask) error {
task.ID = primitive.NewObjectID()
_, err := r.collection.InsertOne(ctx, task)
return err
}
\ That's it! We finished the first method for saving the task. You can implement three more methods, or you can check them out here: Golang Workshop.
Conclusion
In conclusion, we've created a small yet robust task management service using Golang and MongoDB. This exercise demonstrated how Golang's simplicity, concurrency features, and MongoDB's flexibility and scalability provide a powerful platform for building modern web applications.
\ With the right tools and architecture, you can efficiently manage and manipulate data, creating scalable and maintainable services.
\ Now, we know how to build our to-do list and understand that it’s not hard to code.
\ Take care!
This content originally appeared on HackerNoon and was authored by Ilia Ivankin
Ilia Ivankin | Sciencx (2024-07-23T00:48:14+00:00) How to Build Your Own TODO-list Service With Golang and MongoDB. Retrieved from https://www.scien.cx/2024/07/23/how-to-build-your-own-todo-list-service-with-golang-and-mongodb/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.