Cassandra & GoLang
Tags: go, cassandra, opensource, programming
Published: 2021-11-09 12:43:40

Hi there,

Did you read my latest implementation of PostgreSQL with GoLang? [PostgresWithGo](https://dev.to/bseyhan/golang-with-database-operations-3jl0) If you said of course :) go head... Do you know have any knowledge about Cassandra? Don't worry neither am I've not. I like to share about the subjects I work on. So be relax. Are you curious about NOSQl and GoLang? In this post I'll share my Cassandra Db and GoLang knowledge. You'll ask these questions ask yourself, What is Cassandra Db then how to install Cassandra DB to your local machine, which package should I use for it. All of these are in here.  

Step 1 : What is Cassandra DB
Step 2 : How to install it
Step 3 : Important commands 
Step 4 : Connect to database
Step 5 : Implementation
Step 6 : Postman Tests

Let's go to work and I hope you'll enjoy it.

#Step 1: What is Cassandra DB

NoSQL databases are called “Not Only SQL” or “Non-relational” databases. NoSQL databases store and retrieve data other than tabular relations such as relation databases.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bmyxk6y6rzkrcej030s.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vys5minr46plx86znr93.png)
 
Elastic scalability − Cassandra is highly scalable; it allows to add more hardware to accommodate more customers and more data as per requirement.

Always on architecture − Cassandra has no single point of failure and it is continuously available for business-critical applications that cannot afford a failure.

Fast linear-scale performance − Cassandra is linearly scalable, i.e., it increases your throughput as you increase the number of nodes in the cluster. Therefore it maintains a quick response time.

Flexible data storage − Cassandra accommodates all possible data formats including: structured, semi-structured, and unstructured. It can dynamically accommodate changes to your data structures according to your need.

Easy data distribution − Cassandra provides the flexibility to distribute data where you need by replicating data across multiple data centers.

Transaction support − Cassandra supports properties like Atomicity, Consistency, Isolation, and Durability (ACID).

Fast writes − Cassandra was designed to run on cheap commodity hardware. It performs blazingly fast writes and can store hundreds of terabytes of data, without sacrificing the read efficiency.

### Data Types
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iriicx2zds0e5iasyobk.png)
 
#Step 2: How to install 
I'm using mac os. I've already installed Docker application on my machine. So may be you want to install only docker container. [Install Docker](https://docs.docker.com/get-docker/)

`docker pull cassandra`
`docker run -d --name cassandra -p 9042:9042 cassandra`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6y55964thok6pkr9rcsx.png) 

In this repository I created docker-compose.yml file that file contains with required images. 

```

  cassandra:
    image: cassandra:latest
    container_name: cassandra
    ports: 
      - 7000:7000
      - 7001:7001
      - 7199:7199
      - 9042:9042
      - 9160:9160
    restart: always
    environment:
       - CASSANDRA_BROADCAST_ADDRESS=host.docker.internal
       - CASSANDRA_SEEDS=host.docker.internal 
    volumes:
        - ./out/cassandra_data:/var/lib/cassandra
    healthcheck:
        test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"]
        interval: 15s
        timeout: 10s
        retries: 10

```
* 7199 - JMX (was 8080 pre Cassandra 0.8.xx)
* 7000 - Internode communication (not used if TLS enabled)
* 7001 - TLS Internode communication (used if TLS enabled)
* 9160 - Thrift client API
* 9042 - CQL native transport port

Step 3: Important Commands

After the installation since I want to create a database, I have to run this command. What is cqlsh? It is the interface. cqlsh is a command-line interface for interacting with Cassandra using CQL (the Cassandra Query Language). [Documentation](https://cassandra.apache.org/doc/latest/cassandra/tools/cqlsh.html)

`docker exec -it cassandra cqlsh`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqmh5hal7qrmgjg8pmlk.png)
 
Let's create a todo table.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5guk63uvdp5i5txhnmkz.png)

Step 4: Connect to database 

Install the VSCode extension for Cassandra.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t42sby3p4qr5igb61zbq.png)
 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmr2iohg595h1aji4kab.png)

Step 5 : Implementation

Project structure:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d86lssr4ajrpsi3ty7me.png)
 
--> cmd 
  --> api(main.go) this file deal with read config.yml file 
  --> util(config.go & errors.go) config.go is related with application configurations, errors.go is a custom response implementation.

--> pkg
  --> api (api.go) this file integrated to initialize 

```

func Initialize(config utils.Configuration) {

	// Creates a gin router with default middleware:
	router := gin.Default()

	fmt.Printf("%+v\n", config)

	//register database ,repositories and handlers
	session := cassandra.ConnectDatabase(config.Database.Url, config.Database.Keyspace)

	repository := db.NewTodoRepository(session)
	orderHandler := handler.NewTodoHandler(&repository)

	router.GET("/ping", orderHandler.HealthCheck)

	router.POST("/", orderHandler.CreateTodo)
	router.GET("api/v1/todo/:id", orderHandler.GetTodoById)

	//run the server :8080
	router.Run(":8080")
}
```

--> client/cassandra 

The ConnectDatabase function take 2 parameters from api.go file. After creation that function response session. This session

```
func ConnectDatabase(url string, keyspace string) *gocql.Session {

	cluster := gocql.NewCluster(url)
	cluster.Keyspace = keyspace
	cluster.Consistency = gocql.Quorum

	session, _ := cluster.CreateSession()

	return session
}
```
Click fn+12 CreateSession

```
// CreateSession initializes the cluster based on this config and returns a
// session object that can be used to interact with the database.
func (cfg *ClusterConfig) CreateSession() (*Session, error) {
	return NewSession(*cfg)
}
```

-->handler
TodoHandler related with request database and response JSON data also error handling is done for that level. All function take c *gin.Context parameter also these functions has receiver function (t *todoHandler) which means that you can access the repository with t.Save or t.GetById. 

```
func (t *todoHandler) CreateTodo(c *gin.Context) {
	var todo model.Todo

	c.BindJSON(&todo)

	todo.Id = uuid.New().String()
	log.Info("Data is ", todo)
	data, err := t.repo.Save(todo)

	if err != nil {
		c.JSON(http.StatusBadRequest, utils.BadRequestError("insert operation failed!", err))
	}

	c.JSON(http.StatusCreated, gin.H{"todo": data})
}

func (t *todoHandler) GetTodoById(c *gin.Context) {

	id := c.Param("id")

	todo, err := t.repo.GetById(id)
	if err != nil {
		c.JSON(http.StatusBadRequest, utils.BadRequestError("todo not found", err))
	}

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

```
GIN : c.JSON(http.StatusOK, gin.H{"todo": todo}) 
Echo: return c.JSON(http.StatusOK, data)

```
// JSON serializes the given struct as JSON into the response body.
// It also sets the Content-Type as "application/json".
func (c *Context) JSON(code int, obj interface{}) {
	c.Render(code, render.JSON{Data: obj})
}
```

Binding JSON data in GIN

```
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
func (c *Context) BindJSON(obj interface{}) error {
	return c.MustBindWith(obj, binding.JSON)
}
```

In repository layer has "github.com/gocql/gocql" package that's is related the queries and executions. Cassandra DB queries similar to PostgreSQL, MsSQL or MySQL queries.

```
func (t *todoRepository) Save(todo model.Todo) (*model.Todo, error) {

	var query string = "INSERT INTO todo(id,title,content) VALUES(?,?,?)"

	if err := t.session.Query(query, todo.Id, todo.Title, todo.Content).Exec(); err != nil {
		return nil, err
	}

	return &todo, nil
}

func (t *todoRepository) GetById(id string) (*model.Todo, error) {

	var todo model.Todo

	var query string = `SELECT id,title,content FROM todo where id=?`

	if err := t.session.Query(query, id).Scan(&todo.Id, &todo.Title, &todo.Content); err != nil {

		if err == gocql.ErrNotFound {
			return nil, err
		}

		return nil, err
	}

	return &todo, nil
}

```

Now Let's run the docker-compose file. Cassandra DB has own health check configuration. 
`docker-compose up`

```
    healthcheck:
        test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"]
        interval: 15s
        timeout: 10s
        retries: 10
```
When database is running you can see the status is health

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1el38p8u0yoxw2az5s7q.png)


Step 6: Postman Test

- run the program
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ljevv4onefwpdh6bria.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1hmmqeb3d4rfn2i3or8.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rrqt1y7gb2olo99mfc1.png)
  
####References 
[TutorialPoint](https://www.tutorialspoint.com/cassandra/cassandra_introduction.htm)
[Guru99](https://www.guru99.com/cassandra-tutorial.html)

[Github Repo](https://github.com/bburaksseyhan/todo-api)

Thank you.