API Rest con Go (Golang) y PostgreSQL Parte 2
Tags: go, rest, opensource
Published: 2020-05-13 06:15:00

## Requisitos:

* Conocimientos básicos sobre el lenguaje [go](https://golang.org/).
* Conocimiento sobre HTTP y REST recomendable.
* [Go](https://golang.org/dl/) en su versión 1.11 o superior.
* [PostgreSQL](https://www.postgresql.org/download/) en su versión 9.6 o superior.
* Conocimientos básicos de SQL deseables.
* Ganas de aprender.

## Que vamos a desarrollar

Crearemos un API Rest para un microblog, estilo [twitter](https://twitter.com/notifications) en su versión mas básica. Con un modelo de usuarios y uno de publicaciones. Con un [CRUD](https://en.wikipedia.org/wiki/Create%2C_read%2C_update_and_delete) completo en cada caso, autenticación con [JSON Web Tokens](https://jwt.io/), creación, uso de middlewares y asegurar contraseña por con [bcrypt](https://en.wikipedia.org/wiki/Bcrypt). ¿Suena interesante? ¡Entonces vamos!

![Meme](https://media.giphy.com/media/Z6HwUgq2o5aKs/giphy.gif)

### Indice

* [Parte 1 - Preparación](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-m0o)
* **Parte 2 - Modelos**
* [Parte 3 - Base de datos](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-3-29eb)
* [Parte 4 - Manejo de peticiones](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-4-4i31)
* [Parte 5 - Middlewares y JWT](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-5-3p5g)

**Esta es la segunda parte, te recomiendo comenzar por el [articulo anterior](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-m0o).**

## Creando los modelos

### User

Primero crearemos un directorio llamado **user** dentro del directorio **pkg** y, dentro de este nuevo directorio, crearemos un archivo llamado **user.go**.

```bash
mkdir pkg/user && touch pkg/user/user.go
```

Vamos a crear la estructura *User* dentro de este archivo, que va a representar a los usuarios de nuestra API.

```go
// pkg/user/user.go
package user

import "time"

// User of the system.
type User struct {
	ID           uint      `json:"id,omitempty"`
	FirstName    string    `json:"first_name,omitempty"`
	LastName     string    `json:"last_name,omitempty"`
	Username     string    `json:"username,omitempty"`
	Email        string    `json:"email,omitempty"`
	Picture      string    `json:"picture,omitempty"`
	Password     string    `json:"password,omitempty"`
	PasswordHash string    `json:"-"`
	CreatedAt    time.Time `json:"created_at,omitempty"`
	UpdatedAt    time.Time `json:"updated_at,omitempty"`
}
```

Esta estructura contendrá la información de nuestro usuario, bastante sencillo, un identificador único para gestionarlo dentro de la base de datos, información personal, como nombre, apellido y correo electrónico, el **Username**, que utilizaremos para el login y la contraseña, el campo **PasswordHash** sera utilizado para almacenar el hash de la contraseña generado por [bcrypt](https://en.wikipedia.org/wiki/Bcrypt), cosa que haremos en el siguiente paso. También tenemos los campos **CreatedAt** y **UpdatedAt** de tipo ** time.Time** que utilizaremos para mantener un registro de la fecha de cuando fue creado el usuario y cuando fue su ultima modificación. 

Si te resultan extrañas las anotaciones junto a los campos en la estructura, simplemente son una menera conveniente de proporcionar información adicional que, en este caso, usa el paquete [encoding/json](https://golang.org/pkg/encoding/json/) para procesar los campos, como nombrarlos en minúscula y usando notación [snake case](https://en.wikipedia.org/wiki/Snake_case), que omita el campo si esta vació (omitempty), o evitar exponer un campo al convertir la estructura a formato [JSON](https://www.json.org/json-es.html) (json:"-"). Para mas información, les recomiendo consultar [este articulo](https://www.digitalocean.com/community/tutorials/how-to-use-struct-tags-in-go).

Ahora, agregaremos dos métodos a la estructura **User** para gestionar las contraseñas. Pero, primero debemos descargar el paquete [bcrypt](https://godoc.org/golang.org/x/crypto/bcrypt).

```bash
go get golang.org/x/crypto/bcrypt
```
En primer lugar, una función para generar el hash a partir de la contraseña del usuario, para no almacenar esta directamente dentro de la base de datos. Esto se consigue facilmente gracias a la función **GenerateFromPassword** del paquete [bcrypt](https://godoc.org/golang.org/x/crypto/bcrypt) que acabamos de instalar.

```go
func (u *User) HashPassword() error {
	passwordHash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}

	u.PasswordHash = string(passwordHash)

	return nil
}
```

De manera similar, crearemos un método que nos permita comparar el *hash* de la contraseña, almacenada en la propiedad **PasswordHash**, con la contraseña que recibe el método haciendo uso de la función **CompareHashAndPassword** del paquete [bcrypt](https://godoc.org/golang.org/x/crypto/bcrypt).

```go
func (u User) PasswordMatch(password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password))

	return err == nil
}
```

Con esto ya hemos terminado con la estructura **User**. Ahora, dentro del mismo paquete **user** crearemos una **interface** que definirá las operaciones a realizar con el usuario en la base de datos. Esto puede realizarse en el mismo archivo **user.go** o en uno separado mientras comparta el mismo paquete, en este caso lo crearemos en un archivo diferente, ya que lo considero mas ordenado de esa forma, pero es solo una preferencia personal.

```go
// pkg/user/repository.go
package user

import "context"

// Repository handle the CRUD operations with Users.
type Repository interface {
	GetAll(ctx context.Context) ([]User, error)
	GetOne(ctx context.Context, id uint) (User, error)
	GetByUsername(ctx context.Context, username string) (User, error)
	Create(ctx context.Context, user *User) error
	Update(ctx context.Context, id uint, user User) error
	Delete(ctx context.Context, id uint) error
}
```

Esto quizás no parezca tener demasiado sentido en este punto, pero facilita mucho operaciones como [testing](https://en.wikipedia.org/wiki/Software_testing), ademas de que nos permite separar la lógica de los **handlers** de la base de datos, por que podrías usar alguna otra, incluso si es [NoSQL](https://en.wikipedia.org/wiki/NoSQL) como [MongoDB](https://www.mongodb.com/) sin cambios importantes. Pronto estaré compartiendo contenido sobre Go y MongoDB 😉.

Si no están familiarizados con el [paquete context](https://blog.golang.org/context) les recomiendo leer [esta interesante publicación](https://blog.golang.org/context) del [blog de go](https://blog.golang.org/). De cualquier forma, este paquete nos da acceso al tipo de dato **Context** que nos permite definir tiempos limite, cancelar grupos de operaciones, pasar valores a través del contexto, entre otras cosas.

### Post

Ahora crearemos un directorio llamado **post** dentro del directorio **pkg**. Y, dentro de este nuevo directorio crearemos un archivo llamado **post.go**.

```bash
mkdir pkg/post && touch pkg/post/post.go
```

Y crearemos la estructura *Post* dentro de este archivo, que va a representar a las publicaciones de un usuario de nuestra API.

```go
// pkg/post/post.go
package post

import "time"

// Post created by a user.
type Post struct {
	ID        uint      `json:"id,omitempty"`
	Body      string    `json:"body,omitempty"`
	UserID    uint      `json:"user_id,omitempty"`
	CreatedAt time.Time `json:"created_at,omitempty"`
	UpdatedAt time.Time `json:"updated_at,omitempty"`
}
```

Bastante similar al caso anterior. Ahora crearemos también una *interface* **Repository** para esta estructura.

```go
// pkg/post/repository.go
package post

import "context"

// Repository handle the CRUD operations with Posts.
type Repository interface {
	GetAll(ctx context.Context) ([]Post, error)
	GetOne(ctx context.Context, id uint) (Post, error)
	GetByUser(ctx context.Context, userID uint) ([]Post, error)
	Create(ctx context.Context, post *Post) error
	Update(ctx context.Context, id uint, post Post) error
	Delete(ctx context.Context, id uint) error
}
```

Con esto ya tenemos suficiente para pasar a la siguiente fase, la base de datos.

## Conexión con la base de datos.

Lo primero que debemos hacer es conectarnos a [Postgres](https://www.postgresql.org/), pueden usar un cliente de terminal como [psql](https://www.postgresql.org/docs/9.2/app-psql.html) que viene con la instalación de [postgres](https://www.postgresql.org/), alguna interfaz gráfica como [pgAdmin](https://www.pgadmin.org/), lo que les resulte mejor, para el ejemplo usaremos [psql](https://www.postgresql.org/docs/9.2/app-psql.html).

```bash
psql -U postgres
```

En mi caso tengo el usuario de *postgres* sin contraseña, porque es una instalación local que uso para pruebas. Pero es posible que les solicite contraseña para ingresar si establecieron una.

Ahora debemos estar viendo un prompt parecido a este

```bash
postgres=#
```

En espera de comandos. Lo primero que haremos sera crear un nuevo usuario, esto no es obligatorio, perfectamente podríamos trabajar con el usuario *postgres* pero es mejor idea tener un usuario que solo tenga acceso a la base de datos del proyecto por seguridad.

```bash
postgres=# CREATE ROLE gopher WITH LOGIN PASSWORD 'gopher';
```

En el comando de arriba estamos creando un nuevo usuario llamado **gopher**, le estamos dando permiso para hacer *login* y le asignamos la *super segura contraseña* **gopher**. Esto solo para efectos del ejemplo, recomiendo usar una contraseña un poco mas segura 😁.

Al presionar la tecla **ENTER** el comando se ejecutara y deberíamos tener una respuesta indicando: *CREATE ROLE*.

Buen trabajado, ahora crearemos la base de datos que vamos a utilizar en nuestra API.

```bash
postgres=# CREATE DATABASE microblog OWNER gopher;
```

Si todo ha salido bien, tendríamos una respuesta similar a esta: *CREATE DATABASE*. Si desean confirmar que su base de datos fue creada exitosamente, puede listar las bases de datos con el comando **\l** y deberían obtener una respuesta como la siguiente.

```bash
 microblog         | gopher   | UTF8     | en_US.utf8 | en_US.utf8 | 
 postgres          | postgres | UTF8     | en_US.utf8 | en_US.utf8 | 
 template0         | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
                   |          |          |            |            | postgres=CTc/postgres
 template1         | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
                   |          |          |            |            | postgres=CTc/postgres
```
De momento hemos terminado y podemos desconectarnos de [Postgres](https://www.postgresql.org/). Desde [psql](https://www.postgresql.org/docs/9.2/app-psql.html) podemos hacerlo por medio del comando **\q**.

De regreso a nuestro proyecto, crearemos un nuevo directorio llamado **data**, dentro del directorio **internal**, este nuevo directorio debería quedar al mismo nivel que **server**.
```bash
mkdir internal/data && touch internal/data/data.go
```

Y dentro de este nuevo directorio crearemos un archivo llamado **data.go**, que inicialmente contendrá el siguiente código.

```go
package data

import (
	"database/sql"
	"sync"
)

var (
	data *Data
	once sync.Once
)

// Data manages the connection to the database.
type Data struct {
	DB *sql.DB
}
```

Hecho eso, crearemos un nuevo archivo dentro del mismo paquete al que llamaremos **postgres.go**, aquí definiremos la conexión con la base de datos que creamos anteriormente. Antes de avanzar, vamos a descargar un nuevo paquete, en este caso un *driver* para postgres llamado [pq](https://github.com/lib/pq)

```bash
go get github.com/lib/pq
```

Ahora vamos a agregar algo de código a nuestro archivo **postgres.go**.

```go
// internal/data/postgres.go
package data

import (
	"database/sql"

	// registering database driver
	_ "github.com/lib/pq"
)

func getConnection() (*sql.DB, error) {
	uri := "postgres://gopher:gopher@127.0.0.1:5432/microblog?sslmode=disable"
	return sql.Open("postgres", uri)
}
```

El *string* que tenemos dentro de la variable **uri** nos permite conectarnos a la base de datos que creamos anteriormente. Indicando el nombre de usuario, su contraseña, la **IP** y puerto en el que esta corriendo postgres y el nombre de la base de datos a la que queremos conectarnos.

Sin embargo, similar al caso que del puerto, que tratamos en la [publicación anterior](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-m0o), no es conveniente tener esta información directamente dentro del código, así que haremos un par de cambios para obtenera como variable de entorno.

Agregamos una nueva variable a nuestro archivo **.env**.

```bash
# .env
PORT=9000
DATABASE_URI=postgres://gopher:gopher@127.0.0.1:5432/microblog?sslmode=disable
```

Y modificamos el archivo **postgres.go** para leer el valor de esta variable de entorno.

```go
package data

import (
	"database/sql"
	"os"

	// registering database driver
	_ "github.com/lib/pq"
)

func getConnection() (*sql.DB, error) {
	uri := os.Getenv("DATABASE_URI")
	return sql.Open("postgres", uri)
}
```

Ahora regresamos al archivo **data.go** para hacer algunos cambios. Primero, agregamos una función llamada initDB que creara una nueva conexión al base de datos gracias a la función **getConnection** que creamos anteriormente e inicializara el la variable **data** con un nuevo puntero a **Data** que contiene la conexión que acabamos de crear.

```go
// internal/data/data.go
func initDB() {
	db, err := getConnection()
	if err != nil {
		log.Panic(err)
	}

	data = &Data{
		DB: db,
	}
}
```

Una función llamada **New** con la que obtendremos un puntero a **Data**, para evitar crear una nueva conexión a la base de datos cada vez que llamamos a la función **New** utilizamos el tipo **once** del paquete [sync](https://golang.org/pkg/sync/) que forma parte de la biblioteca estándar.

```go
func New() *Data {
	once.Do(initDB)

	return data
}
```

Y agregaremos una función llamada **Close** que usaremos para cerrar los recurso que hayamos abierto, en este caso, la conexión a la base de datos.

Ahora, para comprobar si la conexión se esta realizando correctamente, regresaremos al archivo **main.go** (*cmd/microblog/main.go*), y agregamos la llamada a la función **New** previamente creada y utilizamos la conexión para realizar un **Ping** a la base de datos y, finalmente, usamos la función **Close** para cerrar los recursos una vez que interrumpimos la ejecución del programa.

```go
// cmd/microblog/main.go
package main

import (
	"os"
	"os/signal"

	"log"

	"github.com/orlmonteverde/go-postgres-microblog/internal/data"
	"github.com/orlmonteverde/go-postgres-microblog/internal/server"

	_ "github.com/joho/godotenv/autoload"
)

func main() {
	port := os.Getenv("PORT")
	serv, err := server.New(port)
	if err != nil {
		log.Fatal(err)
	}

	// connection to the database.
	d := data.New()
	if err := d.DB.Ping(); err != nil {
		log.Fatal(err)
	}

	// start the server.
	go serv.Start()

	// Wait for an in interrupt.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c

	// Attempt a graceful shutdown.
	serv.Close()
	data.Close()
}
```

Por ahora lo dejaremos hasta aquí, en el siguiente articulo continuaremos con el desarrollo de nuestra API. Les comparto el [repositorio](https://github.com/orlmonteverde/go-postgres-microblog/tree/v1.1.0) con el código del proyecto hasta el momento.

Realmente espero que el contenido les sea de utilidad, y cualquier duda o sugerencia, por favor, déjenla en los comentarios y les responderé tan pronto como me resulte posible. Saludos y gracias por leer.

![konosuba](https://media.giphy.com/media/Zz7susxfOWFb2/giphy.gif)