API Rest con Go (Golang) y PostgreSQL Parte 3
Tags: go, rest, opensource
Published: 2020-05-20 08:06:30

## 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](https://en.wikipedia.org/wiki/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](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-2-137l)
* **Parte 3 - Base de datos**
* [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 tercera parte, recomiendo comenzar por el [primer articulo](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-m0o). O si gustas revisa el [articulo anterior](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-2-137l)**. De cualquier forma, el código del que vamos a partir en este artículo se encuentra en este [repositorio](https://github.com/orlmonteverde/go-postgres-microblog/tree/v1.1.0).


## Repositorios

Ahora vamos a crear *script* de **[SQL](https://en.wikipedia.org/wiki/SQL)** para crear las tablas correspondientes a nuestros modelos: **user** y **post**. No me detendré a explicar detalladamente el código porque no es el propósito de esta publicación, si gustas puedes darte una vuelta por [este tutorial](https://www.w3schools.com/sql/).

Si ya conoces **[SQL](https://en.wikipedia.org/wiki/SQL)** seguro sera fácil de entender y en caso contrario simplemente estamos creando una tabla llamada **users**, con columnas que corresponden a los campos de nuestra estructura **user**. De igual forma, una tabla **posts**, que corresponde a nuestra estructura **post**, que se relaciona con los usuarios por medio del campo **user_id**, que corresponde a un **id** de usuario.

```sql
CREATE TABLE IF NOT EXISTS users (
    id serial NOT NULL,
    first_name VARCHAR(150) NOT NULL,
    last_name VARCHAR(150) NOT NULL,
    username VARCHAR(150) NOT NULL UNIQUE,
    password varchar(256) NOT NULL,
    email VARCHAR(150) NOT NULL UNIQUE,
    picture VARCHAR(256) NOT NULL,
    created_at timestamp DEFAULT now(),
    updated_at timestamp NOT NULL,
    CONSTRAINT pk_users PRIMARY KEY(id)
);

CREATE TABLE IF NOT EXISTS posts (
    id serial NOT NULL,
    user_id int NOT NULL,
    body text NOT NULL,
    created_at timestamp DEFAULT now(),
    updated_at timestamp NOT NULL,
    CONSTRAINT pk_notes PRIMARY KEY(id),
    CONSTRAINT fk_posts_users FOREIGN KEY(user_id) REFERENCES users(id)
);
```

Seria posible utilizar este código en nuestro motor de base de datos bien sea copiando y pegando o ejecutándolo directamente, pero vamos colocarlo dentro de un directorio llamado **database** en la raíz de nuestro proyecto y a darle el nombre de **models.sql** por un lado para que tengan acceso al archivo y por otro para que nuestra aplicación pueda leerlo y encargarse de ejecutarlo.

Regresamos al archivo **postgres.sql** y realizamos algunos cambios. Importamos el paquete **[ioutil](https://golang.org/pkg/io/ioutil/)** de la biblioteca estándar, que nos permitirá leer el archivo y agregamos una nueva función a la que llamamos **MakeMigration**, que se encargara de leer nuestro archivo **models.sql** y de ejecutar las instrucciones que escribimos en el.

```go
// internal/data/postgres.go
// […]

func MakeMigration(db *sql.DB) error {
	b, err := ioutil.ReadFile("./database/models.sql")
	if err != nil {
		return err
	}

	rows, err := db.Query(string(b))
	if err != nil {
		return err
	}

	return rows.Close()
}
```

Ahora debemos ejecutar esta función, vamos a hacerlo en la funcion initDB que creamos antes, en el archivo **data.go**

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

	err = MakeMigration(db)
	if err != nil {
		log.Panic(err)
	}

	data = &Data{
		DB: db,
	}
}
[…]
```

Hecho esto, vamos a compilar y ejecutar nuevamente nuestro programa para comprobar que, hasta este punto, todo este funcionando como esperamos.

```bash
go build ./cmd/microblog && ./microblog
```

Si no tenemos errores, el programa debería compilar, ejecutarse y mostrar el mensaje de que el servidor esta corriendo, similar al siguiente.

```bash
2020/05/19 17:28:21 Server running on http://localhost:9000
```

Y si revisamos la base de datos, deberíamos tener las tablas correctamente creadas.

```bash
 Schema |     Name     |   Type   | Owner  
--------+--------------+----------+--------
 public | posts        | table    | gopher
 public | posts_id_seq | sequence | gopher
 public | users        | table    | gopher
 public | users_id_seq | sequence | gopher
```

### Usuarios

Ahora que tenemos nuestras tablas creadas procederemos con las operaciones [CRUD](https://en.wikipedia.org/wiki/Create%2C_read%2C_update_and_delete) para nuestro usuario. Dentro del paquete **data** crearemos una estructura llamada **UserRepository** a la que agregaremos los métodos necesarios para satisfacer la interfaz **Repository** que creamos anteriormente dentro del paquete **user**.

Esta nueva estructura podemos colocarla donde nos parezca mas conveniente, mientras se encuentre dentro del paquete **data**. Vamos a crearla en un nuevo archivo llamado **user_repository.go** porque, en mi opinión, es mas cómodo y organizado.

```go
// internal/data/user_repository.go
package data

type UserRepository struct {
	Data *Data
}
```

#### Obtener todos

Comencemos por el método **GetAll**, que nos permitirá obtener todos los usuarios en la tabla **users**.

```go
// internal/data/user_repository.go
// […]
func (ur *UserRepository) GetAll(ctx context.Context) ([]user.User, error) {
	q := `
	SELECT id, first_name, last_name, username, email, picture,
		created_at, updated_at
		FROM users;
	`

	rows, err := ur.Data.DB.QueryContext(ctx, q)
	if err != nil {
		return nil, err
	}

	defer rows.Close()

	var users []user.User
	for rows.Next() {
		var u user.User
		rows.Scan(&u.ID, &u.FirstName, &u.LastName, &u.Username,
			&u.Email, &u.Picture, &u.CreatedAt, &u.UpdatedAt)
		users = append(users, u)
	}

	return users, nil
}
```

En primer lugar, definimos la consulta dentro de la variable que llamamos **q**, la ejecutamos por medio del método **QueryContext**, lo que nos retorna un valor de tipo puntero **sql.Rows**, el cual nos aseguramos de cerrar con su metodo **Close** gracias a **defer**. Entonces definimos una variable **users** que sera donde almacenaremos los usuarios y utilizamos un ciclo **for** en conjunto con el método **Next**, que retorna un *Bool* que sera *true* mientras existan filas por leer.

Dentro de este ciclo definimos una variable llama **u** de tipo **user.User** donde almacenaremos la información de cada usuario leído mediante el método **Scan** del tipo **sql.Rows** para posteriormente agregarlo a **users**. Finalmente, retornamos la colección de usuarios y y el valor **nil** para indicar que el proceso concluyo satisfactoriamente.

#### Obtener uno

Este método es muy similar al caso anterior, solo que retornamos un único usuario.

```go
// internal/data/user_repository.go
// […]
func (ur *UserRepository) GetOne(ctx context.Context, id uint) (user.User, error) {
	q := `
	SELECT id, first_name, last_name, username, email, picture,
		created_at, updated_at
		FROM users WHERE id = $1;
	`

	row := ur.Data.DB.QueryRowContext(ctx, q, id)

	var u user.User
	err := row.Scan(&u.ID, &u.FirstName, &u.LastName, &u.Username, &u.Email,
		&u.Picture, &u.CreatedAt, &u.UpdatedAt)
	if err != nil {
		return user.User{}, err
	}

	return u, nil
}
```

No hay mucho que destacar en este fragmento de código, es bastante similar al anterior, con excepción de que, en este caso, vamos a pasar argumentos a la consulta, cosa que indicamos con la sintaxi **${numero correspondiente al orden en el que se pasa el argumento}**, esto varia según el motor de base de datos que se este utilizando, en este caso es valido para [PostgreSQL](https://www.postgresql.org/), pero en el caso de, por ejemplo, [MySQL](https://www.mysql.com/) o [SQLite](https://sqlite.org/index.html) se utiliza **?** en su lugar.

Otro detalle a destacar es el uso del método **QueryRowContext** en lugar de **QueryContext** que utilizamos anteriormente, esto se debe a que esta consulta solo retornara una fila. Ademas, esta vez estamos pasando como tercer argumento el **id** del usuario, que tomara el lugar del **$1** que definimos dentro del *string* que almacenamos en **q**.

#### Obtener por username

De manera casi idéntica al caso anterior, vamos a retornar un único usuario, solo que esta vez mediante su **username** en lugar de su **id**.

```go
// internal/data/user_repository.go
// […]
func (ur *UserRepository) GetByUsername(ctx context.Context, username string) (user.User, error) {
	q := `
	SELECT id, first_name, last_name, username, email, picture,
		password, created_at, updated_at
		FROM users WHERE username = $1;
	`

	row := ur.Data.DB.QueryRowContext(ctx, q, username)

	var u user.User
	err := row.Scan(&u.ID, &u.FirstName, &u.LastName, &u.Username,
		&u.Email, &u.Picture, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt)
	if err != nil {
		return user.User{}, err
	}

	return u, nil
}
```

¿Pueden encontrar las diferencias con el método **GetOne**? 😁. Sino, no se preocupen, son realmente parecidas, la diferencia mas destacable seria el uso del **username** en lugar del **id**, otro, quizás menos visible, es que ahora estamos solicitando la columna **password** en la consulta y, a su vez, ese valor lo estamos almacenando en el campo **PasswordHash** del usuario.

La razón por la que estamos solicitando el la **contraseña** esta vez es porque el método **GetByUsername** sera utilizado mas adelante para manejar el ingreso de usuarios y sera necesario comparar la contraseña con la que tenemos almacenada en la base de datos.

#### Insertar

Ahora es momento de agregar nuevos usuarios a la tabla, para ese fin preparamos la consulta correspondiente, esta vez utilizando **[insert into](https://www.w3schools.com/sql/sql_insert.asp)**. 

```go
// internal/data/user_repository.go
// […]
func (ur *UserRepository) Create(ctx context.Context, u *user.User) error {
	q := `
	INSERT INTO users (first_name, last_name, username, email, picture, password, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
		RETURNING id;
	`

	if u.Picture == "" {
		u.Picture = "https://placekitten.com/g/300/300"
	}

	if err := u.HashPassword(); err != nil {
		return err
	}

	row := ur.Data.DB.QueryRowContext(
		ctx, q, u.FirstName, u.LastName, u.Username, u.Email,
		u.Picture, u.PasswordHash, time.Now(), time.Now(),
	)

	err := row.Scan(&u.ID)
	if err != nil {
		return err
	}

	return nil
}
```

Realizamos una pequeña validación para agregar una imagen por defecto si el usuario no incluye una, en este caso un simpático gato desde [placekitten](https://placekitten.com/). Generamos el  **PasswordHash** gracias al método **HashPassword** que creamos en el [primer articulo](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-m0o) y ejecutamos la sentencia para, finalmente, leer el valor del **id** que le fue asignado al nuevo usuario ya que lo usaremos mas adelante.

#### Actualizar

Ahora que podemos obtener y crear usuarios vamos a agregar un método que nos permita actualizar un usuario existente.

```go
// internal/data/user_repository.go
// […]
func (ur *UserRepository) Update(ctx context.Context, id uint, u user.User) error {
	q := `
	UPDATE users set first_name=$1, last_name=$2, email=$3, picture=$4, updated_at=$5
		WHERE id=$6;
	`

	stmt, err := ur.Data.DB.PrepareContext(ctx, q)
	if err != nil {
		return err
	}

	defer stmt.Close()

	_, err = stmt.ExecContext(
		ctx, u.FirstName, u.LastName, u.Email,
		u.Picture, time.Now(), id,
	)
	if err != nil {
		return err
	}

	return nil
}
```

Para actualizar utilizamos la sentencia [SQL](https://en.wikipedia.org/wiki/SQL) **[update](https://www.w3schools.com/sql/sql_update.asp)** y, de esta forma, actualizamos todos los campos del usuarios (con excepción de id, username, password y created_at) que corresponda con el **id** dado.

### Borrar

Ahora es momento de borrar un usuario, la operación restante para completar nuestro [CRUD](https://en.wikipedia.org/wiki/Create%2C_read%2C_update_and_delete).

```go
// internal/data/user_repository.go
// […]
func (ur *UserRepository) Delete(ctx context.Context, id uint) error {
	q := `DELETE FROM users WHERE id=$1;`

	stmt, err := ur.Data.DB.PrepareContext(ctx, q)
	if err != nil {
		return err
	}

	defer stmt.Close()

	_, err = stmt.ExecContext(ctx, id)
	if err != nil {
		return err
	}

	return nil
}
```

Este método es uno de los mas sencillos, simplemente usamos la sentencia [SQL](https://en.wikipedia.org/wiki/SQL) **[delete](https://www.w3schools.com/sql/sql_delete.asp)** para eliminar al usuario cuyo **id** corresponda con el dado. La clausula **[where](https://www.w3schools.com/sql/sql_where.asp)** es muy importante para esto ultimo, de otra forma eliminaríamos todos los usuarios en la tabla y probablemente no es lo que buscamos 😜.

### Publicaciones

Ahora es momento del [CRUD](https://en.wikipedia.org/wiki/Create%2C_read%2C_update_and_delete) para las publicaciones. De manera similar al como lo hicimos para **user** crearemos un archivo llamado **post_repository.go** dentro del paquete **data** y lo primero que haremos sera agregar una estructura que llamaremos **PostRepository**.

```go
// internal/data/post_repository.go
package data

type PostRepository struct {
	Data *Data
}
```

Para esta estructura, como podrán suponer por el caso anterior, agregaremos los métodos correspondientes para satisfacer la interfaz **Repository** en el paquete **post** que definimos en el [primer articulo](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-m0o).

Debido a que todo el proceso es prácticamente igual al que realizamos para el caso de los usuarios solo me detendré a explicar el método para *obtener todos las publicaciones de un usuario* que considero ligeramente diferente a los ya vistos y para los demás casos voy a compartir el código ¿todos a favor?

![thumbs up](https://i.giphy.com/media/t6AXtEOV8R0nS/giphy.gif)


#### Obtener todos

```go
// internal/data/post_repository.go
// […]
func (pr *PostRepository) GetAll(ctx context.Context) ([]post.Post, error) {
	q := `
	SELECT id, body, user_id, created_at, updated_at
		FROM posts;
	`

	rows, err := pr.Data.DB.QueryContext(ctx, q)
	if err != nil {
		return nil, err
	}

	defer rows.Close()

	var posts []post.Post
	for rows.Next() {
		var p post.Post
		rows.Scan(&p.ID, &p.Body, &p.UserID, &p.CreatedAt, &p.UpdatedAt)
		posts = append(posts, p)
	}

	return posts, nil
}
```

#### Obtener uno

```go
// internal/data/post_repository.go
// […]
func (pr *PostRepository) GetOne(ctx context.Context, id uint) (post.Post, error) {
	q := `
	SELECT id, body, user_id, created_at, updated_at
		FROM posts WHERE id = $1;
	`

	row := pr.Data.DB.QueryRowContext(ctx, q, id)

	var p post.Post
	err := row.Scan(&p.ID, &p.Body, &p.UserID, &p.CreatedAt, &p.UpdatedAt)
	if err != nil {
		return post.Post{}, err
	}

	return p, nil
}
```

#### Obtener todas las publicaciones de un usuario

Este método es bastante parecido al de **GetAll**, con la diferencia de que no obtendremos todas las publicaciones en la tabla sino que las filtraremos por el **id** de un usuario, agregando la clausula [SQL](https://en.wikipedia.org/wiki/SQL) **[where](https://www.w3schools.com/sql/sql_where.asp)** al final de la consulta.

```go
// internal/data/post_repository.go
// […]
func (pr *PostRepository) GetByUser(ctx context.Context, userID uint) ([]post.Post, error) {
	q := `
	SELECT id, body, user_id, created_at, updated_at
		FROM posts
		WHERE user_id = $1;
	`

	rows, err := pr.Data.DB.QueryContext(ctx, q, userID)
	if err != nil {
		return nil, err
	}

	defer rows.Close()

	var posts []post.Post
	for rows.Next() {
		var p post.Post
		rows.Scan(&p.ID, &p.Body, &p.UserID, &p.CreatedAt, &p.UpdatedAt)
		posts = append(posts, p)
	}

	return posts, nil
}
```

#### Insertar

```go
// internal/data/post_repository.go
// […]
func (pr *PostRepository) Create(ctx context.Context, p *post.Post) error {
	q := `
	INSERT INTO posts (body, user_id, created_at, updated_at)
		VALUES ($1, $2, $3, $4)
		RETURNING id;
	`

	stmt, err := pr.Data.DB.PrepareContext(ctx, q)
	if err != nil {
		return err
	}

	defer stmt.Close()

	row := stmt.QueryRowContext(ctx, p.Body, p.UserID, time.Now(), time.Now())

	err = row.Scan(&p.ID)
	if err != nil {
		return err
	}

	return nil
}
```

#### Actualizar

```go
// internal/data/post_repository.go
// […]
func (pr *PostRepository) Update(ctx context.Context, id uint, p post.Post) error {
	q := `
	UPDATE posts set body=$1, updated_at=$2
		WHERE id=$3;
	`

	stmt, err := pr.Data.DB.PrepareContext(ctx, q)
	if err != nil {
		return err
	}

	defer stmt.Close()

	_, err = stmt.ExecContext(
		ctx, p.Body, time.Now(), id,
	)
	if err != nil {
		return err
	}

	return nil
}
```

#### Borrar

```go
// internal/data/post_repository.go
// […]
func (pr *PostRepository) Delete(ctx context.Context, id uint) error {
	q := `DELETE FROM posts WHERE id=$1;`

	stmt, err := pr.Data.DB.PrepareContext(ctx, q)
	if err != nil {
		return err
	}

	defer stmt.Close()

	_, err = stmt.ExecContext(ctx, id)
	if err != nil {
		return err
	}

	return nil
}
```

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.2.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/9VSxGCayIge1a/giphy.gif)