Building a Simple TODO App with Gin-gonic in Zerops: A step-by-step Guide
Tags: cloud, beginners, go
Published: 2022-09-23 07:42:45

Building APIs is bread and butter for most programmers. The [Zerops](https://contabo.com/en/zerops/) app platform makes this process even easier by taking care of the infrastructure.

In this article, we will show you how simple it is to build a sample CRUD TODO API written in GO using gin-gonic, one of the most popular web frameworks, which saves your data to the PostgreSQL database.

The resulting project is available [here](https://github.com/zeropsio/recipe-gin-postgres-api).

## Getting started
First and foremost we need a database system to store and manage our data. In this case, we chose **PostgreSQL**.

_Side note: If you prefer a different technology, such as **MariaDB**, which Zerops also supports, the process is analogous and the source code can be found [here](https://github.com/zeropsio/recipe-gin-maria-api)._

Let's start by running a PostgreSQL server. We can create one very easily in Zerops, either manually or via the Zerops import function. Simply create a new project and add a PostgreSQL service ([here’s more about PostgreSQL in Zerops](https://docs.contabozerops.com/documentation/services/databases/postgresql.html#adding-the-postgresql-service-in-zerops)).

To test the connection or for local development, we can connect to the server with Zerops’ own command line utility called zCLI. After you have [installed](https://docs.contabozerops.com/documentation/cli/installation.html#how-to-install) the zCLI, follow [these simple steps](https://docs.contabozerops.com/documentation/cli/authorization.html#login-using-personal-token) to log in using a personal access token. The last step is to start a VPN connection by running

```sh
$ zcli vpn start [projectName]
```

Now we should be able to test that the server is accessible by running

```sh
$ ping6 [hostname].zerops # in our case postgresql0.zerops
```

## Golang

### Dependencies

We will create the API as a GO module for easier versioning and reproducibility of builds. New GO modules are created with:

```sh
$ go mod init [api-name]
```

This command creates two files, `go.mod` and `go.sum`, which both contain dependency information.

The following GO packages are used in this example:

* [github.com/georgysavva/scany (v1.1.0)](https://github.com/georgysavva/scany)
* [github.com/gin-contrib/cors (v1.4.0)](https://github.com/gin-contrib/cors)
* [github.com/gin-gonic/gin (v1.8.1)](https://github.com/gin-gonic/gin)
* [github.com/jackc/pgx/v4 (v4.17.1)](https://github.com/jackc/pgx/v4)

and they can be installed using

```sh
$ go get [package-url]
```

More information on how to use _go modules_ can be found [here](https://go.dev/blog/using-go-modules).

### Folder structure

This being a sample application, the project structure is very simple.

```txt
todo-api/
├── http.go
├── main.go
├── model.go
├── go.mod
├── go.sum
├── schema.sql
└── zerops.yml
```

The API source code is contained in the following files:

* `http.go` - relating to the HTTP server
* `model.go` - for communication with the DB
* `main.go` - initialization and wiring of dependencies together

This is the bootstrap of the Gin framework which is enough for it to run in Zerops. The following is our implementation of the HTTP server.

First of all, we need to initialize the server in `main.go` by calling

```go
r := gin.Default()
```

To run the HTTP server smoothly, and not just in Zerops, we use several middlewares. They include CORS support, better error logging that logs to the Zerops runtime log, and a content-type header addition, which is an example of custom-written middleware.

To see the logs in Zerops runtime log, we need to add the following middleware to the gin server. It handles errors and returns 500/4xx error codes depending on the error type. Additionally, it logs failed http requests with 500 to the stdout for Zerops to render on the frontend.

```go
r.Use(gin.ErrorLoggerT(gin.ErrorTypePublic | gin.ErrorTypeBind))
```
Now that we are done with the basic server setup, all that’s left is to register the endpoints. First, we create a router group that will consist of routes with the same path prefix.

```go
g := r.Group("todos")
```

This API contains CRUD operations for working with the `todo` resource. We register each `url` path to a handler, which processes the corresponding HTTP request.

```go
g.GET("", handler.getTodos)
g.GET("/:id", handler.getTodo)
g.POST("", handler.createTodo)
g.PATCH("/:id", handler.editTodo)
g.DELETE("/:id", handler.deleteTodo)
```

We have chosen the `getTodos` handler as an example in this blog post. For the full list of handlers, consult the `http.go` file.

```go
func (t todoHandler) getTodos(c *gin.Context) {
    todos, err := t.model.FindAll(c.Request.Context())
    if err != nil {
   	 internalServerError(c, err)
   	 return
    }
    c.JSON(http.StatusOK, todos)
}
```

Finally, we can run this server on port `3000` using the following code in the `main.go`.

```go
log.Fatal(r.Run(":3000"))
```

## Running the API locally

In the main.go file there are 3 environment variables used to connect, migrate, and seed the database. We can do this by creating an .env file with following content:

```env
ZEROPS_RECIPE_DATA_SEED=["Buy milk", "Write Zerops article"]
ZEROPS_RECIPE_DROP_TABLE=1
DB_URL=postgres://${user}:${password}@${hostname}:5432/${hostname}
```

To get the `user`, `password` and `hostname` values, see [environment variables](https://docs.contabozerops.com/documentation/environment-variables/overview.html#referencing-environment-variables) in Zerops GUI.

Make sure you have the zCLI VPN up and running to proceed here. 
Run this command to set the environment variables and run the API:

```sh
$ source .env && go run main.go http.go model.go
```

## Running the API in Zerops

After we completed the development of the API, and tested its functionality locally, it's time to deploy it to Zerops. To do that, we need to create a configuration file called `zerops.yml`, which contains steps to build and deploy our app. For GO, this file is rather simple and looks like this:

```yaml
api:
  build:
    base: [ go@1 ]
    build:
      - go build -o app main.go model.go http.go
    deploy: [ app ]
  run:
    start: ./app    
```

The simplest way to deploy our API to Zerops is to integrate it as an external [GitHub](https://docs.contabozerops.com/documentation/github/github-integration.html) or [GitLab](https://docs.contabozerops.com/documentation/gitlab/gitlab-integration.html) repository. We need to create a [Golang](https://docs.contabozerops.com/documentation/services/runtimes/golang.html#adding-the-golang-service-in-zerops) service in Zerops. The easiest way to configure the API is to paste the environment variables in the _.env_ file format, defined in _Running the API locally_. 

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

Once the service is created, we enable a subdomain, which is a domain used for development and testing purposes. When we access it, we will see a response with todo entries from the `ZEROPS_RECIPE_DATA_SEED` variable.

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

That's it!

## Conclusion

Hopefully, you managed to follow this article and deploy the API to Zerops successfully. If you have any further questions, visit our [Discord channel](https://discord.com/invite/WDvCZ54).