API Rest con Go (Golang) y PostgreSQL Parte 4
Tags: opensource, go, api
Published: 2020-06-04 06:17:12

## 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](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-3-29eb)
* **Parte 4 - Manejo de peticiones**
* [Parte 5 - Middlewares y JWT](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-5-3p5g)

[Código fuente](https://github.com/orlmonteverde/go-postgres-microblog/tree/v1.2.0) hasta el momento.

## El paquete response

Comencemos creando un nuevo paquete al que llamaremos **response**, en este agruparemos algunas funcionalidades útiles para las respuestas de nuestras funciones **handler**, podríamos utilizar, por ejemplo el paquete [render](https://github.com/go-chi/render) perteneciente al ecosistema de [chi](https://github.com/go-chi/chi), que es el paquete que estamos utilizando para manejar las rutas, o algún equivalente, pero en nuestro caso no seria necesario descargar una dependencia adicional si perfectamente podemos cubrir nuestra necesidad con unas pocas lineas de código. Dicho esto, vamos a crear nuestro nuevo paquete.

```bash
mkdir pkg/response && touch $_/response.go
```

Y agregamos el siguiente contenido a nuestro paquete.

```go
// pkg/response/response.go
package response

type ErrorMessage struct {
	Message string `json:"message"`
}
```

Con este tipo, **ErrorMessage** podemos estandarizar las respuestas de error, solo contiene un campo **Message** de tipo *string* que representa el mensaje de error.

```go
// pkg/response/response.go
// [...]

type Map map[string]interface{}
```

**Map** es simplemente un **map** con claves de tipo *string* y valor de *interface* vacía, que, a efectos prácticos nos permite almacenar valores de cualquier tipo. Este tipo nos facilita el trabajo de [serializar/deserializar](https://code.tutsplus.com/tutorials/json-serialization-with-golang--cms-30209), lo veremos en la practica en breve.

```go
// pkg/response/response.go
// [...]

func JSON(w http.ResponseWriter, r *http.Request, statusCode int, data interface{}) error {
	if data == nil {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(statusCode)
		return nil
	}

	j, err := json.Marshal(data)
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(statusCode)
	w.Write(j)
	return nil
}
```

Con la función **JSON** podemos realizar respuestas en formato [JSON](https://www.json.org/json-en.html), evitando repetir constantemente la parte de [serializar](https://code.tutsplus.com/tutorials/json-serialization-with-golang--cms-30209) la **data**, y sinceramente lo vamos a ocupar bastante 😜. En caso de que el argumento **data** sea diferente de *nil* lo [serializamos](https://code.tutsplus.com/tutorials/json-serialization-with-golang--cms-30209) como [JSON](https://www.json.org/json-en.html) y lo escribimos en la respuesta.

```go
// pkg/response/response.go
// [...]

func HTTPError(w http.ResponseWriter, r *http.Request, statusCode int, message string) error {
	msg := ErrorMessage{
		Message: message,
	}

	return JSON(w, r, statusCode, msg)
}
```

En lo que respecta a la función **HTTPError** es solo una manera conveniente de crear un nuevo **ErrorMessage** a partir del **message** que recibe y luego enviarlo como respuesta [JSON](https://www.json.org/json-en.html) gracias a la función **JSON** que creamos antes.

Si algo de lo anterior no les ha quedado claro estoy seguro de que sera mas fácil de entender cuando lo veamos en acción, cosa que haremos de inmediato. Es momento de mi parte favorita, manejar las peticiones 😁.

## Manejar las peticiones

Si recuerdan en el [primer articulo](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-m0o) creamos el paquete **server** (*internal/server*), en el que dejamos listas algunas configuraciones, ahora es momento de regresar a ese paquete y agregar nuestros *handlers*.

Para comenzar vamos a crear un paquete al que llamaremos **v1** dentro del paquete **server**, es decir, sera una carpeta dentro de este ultimo. No es realmente necesario trabajar los *handlers* en un paquete separado de **server**, pero si a futuro debemos agregar una nueva versión de nuestra **API** sera mas conveniente tenerlo organizado de esta forma. Pero esa es solo mi opinión y no la verdad absoluta, siéntanse libres de probar, buscar y compartir cualquier otra opción o idea al respecto.

```bash
mkdir internal/server/v1
```

### Rutas de usuario

En primer lugar crearemos un nuevo archivo dentro del directorio **v1** que creamos recientemente, a este archivo lo llamaremos **user_router.go**.

```bash
touch internal/server/v1/user_router.go
```

Y agregamos en este nuevo archivo el siguiente contenido.

```go
// internal/server/v1/user_router.go
package v1

import "github.com/orlmonteverde/go-postgres-microblog/pkg/user"

type UserRouter struct {
	Repository user.Repository
}
```

Esta estructura que llamamos **UserRouter** contiene el campo **Repository**, que es del tipo **user.Repository**, este tipo es una interfaz creada en la [segunda parte](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-2-137l), cuyos métodos coinciden con el tipo **UserRepository** que creamos en la [tercera parte](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-3-29eb). Esta no es una casualidad, se realizo intencionalmente para poder usar **UserRepository** como **user.Repository**.

Es posible que se estén preguntando que caso tiene usar la interfaz en lugar de directamente utilizar **UserRepository**, en nuestro caso no habría mucha diferencia, pero utilizar la interfaz brinda varias ventajas, principalmente hace fácil remplazar el **Repository** por cualquier tipo que satisfaga la interfaz, por ejemplo otra implementación utilizando un motor de bases de datos diferente, o un tipo falso creado solo para propósitos de [testing](https://en.wikipedia.org/wiki/Software_testing)

#### Crear un usuario

Comencemos por el método de crear usuario, el objetivo es recibir un usuario en formato [JSON](https://www.json.org/json-en.html) desde el cuerpo de la petición [deserializarlo](https://code.tutsplus.com/tutorials/json-serialization-with-golang--cms-30209) en una variable de tipo **user.User** y almacenarlo en nuestra base de datos. 

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) CreateHandler(w http.ResponseWriter, r *http.Request) {
	var u user.User
	err := json.NewDecoder(r.Body).Decode(&u)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	defer r.Body.Close()

	ctx := r.Context()
	err = ur.Repository.Create(ctx, &u)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	u.Password = ""
	w.Header().Add("Location", fmt.Sprintf("%s%d", r.URL.String(), u.ID))
	response.JSON(w, r, http.StatusCreated, response.Map{"user": u})
}
```

En primer lugar estamos definiendo una variable **u** de tipo **user.User**, luego usamos la función **NewDecoder** del paquete [json](https://golang.org/pkg/encoding/json/), de la librería estándar para obtener un puntero **json.Decoder**, que usamos inmediatamente para [deserializar](https://code.tutsplus.com/tutorials/json-serialization-with-golang--cms-30209) la data dentro de la variable **u**, es importante que sea un puntero porque necesita acceder a su lugar en memoria y realizar cambios en su valor.

Como pueden ver, estamos utilizando la función **HTTPError** del paquete **response** que creamos en un inicio para enviar una respuesta de error en caso de que exista, este proceso lo estaremos realizando frecuentemente en los *handlers*.

Creamos el usuario utilizando el método **Create** de **Repository**, al cual le pasamos un **context.Context**, en este caso el **Context** del **Request**, y un puntero a nuestra variable **u** que es ese punto contiene los datos del usuario a crear. Comprobamos si existe un error, de manera similar a lo hecho anteriormente.

Cambiamos el valor de la contraseña del usuario a un *string* vació para que el campo sea ignorado al generar el [JSON](https://www.json.org/json-en.html). Agregamos a la cabecera de la respuesta el campo **Location** dando como valor la dirección del recurso que acabamos de crear, esto ultimo no es obligatorio pero es una buena practica. Para, finalmente, enviar una respuesta de [JSON](https://www.json.org/json-en.html), gracias a la función **JSON** del paquete **response** que creamos al inicio (les dije que seria útil 😁) con el código de estado **201** (created).

Podrán notar que como **data** no estamos enviando directamente el usuario sino que este es enviado como valor de un campo llamado **user** en el **response.Map**, yo acostumbro enviarlo de esa forma pero si prefieren enviar el usuario directamente es valido.

#### Obtener usuarios

Ahora que ya podemos crear usuario veamos como obtener los usuarios ya registrados.

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) GetAllHandler(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	users, err := ur.Repository.GetAll(ctx)
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, response.Map{"users": users})
}
```

En este caso no necesitamos información proveniente del usuario, utilizamos el método **GetAll** de **Repository** para obtener todos los usuarios, comprobamos la existencia de un error y enviamos una respuesta de tipo [JSON](https://www.json.org/json-en.html) con los usuarios obtenidos, esta vez con el código **200** (OK).

#### Obtener un usuario

Con el método **GetAllHandler** podemos obtener todos los usuarios, pero algunas veces solo necesitamos la información de un usuario, así que vamos a crear un *handler* que nos permita hacer eso.

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) GetOneHandler(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")

	id, err := strconv.Atoi(idStr)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	ctx := r.Context()
	u, err := ur.Repository.GetOne(ctx, uint(id))
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, response.Map{"user": u})
}
```

Para obtener un usuario especifico es necesaria una manera de identificarlo, en nuestro caso la mejor opción es el **id** asignado por la base de datos. Entonces necesitamos obtener ese **id** en a petición de alguna manera, considerando que sera una petición **GET** y no se debería enviar información por el **Body**. Una manera muy común de realizar esto es por medio de [Query](https://en.wikipedia.org/wiki/Query_string) o como parámetros en la [URL](https://en.wikipedia.org/wiki/URL), vamos utilizar este ultimo, y [chi](https://github.com/go-chi/chi) nos facilita el trabajo gracias la función **URLParam** con la que podemos obtener el valor de un parámetro de la [URL](https://en.wikipedia.org/wiki/URL) del **http.Request**.

Es importante resaltar que la función **URLParam** retorna un *string*, pero nosotros necesitamos el valor como un *int* (como un *uint* realmente pero esa es una conversión sencilla 😀), cosa que podemos realizar fácilmente con la función **Atoi** del paquete [strconv](https://golang.org/pkg/strconv/).

El resto es muy similar a lo que hicimos en el *handler* **GetAllHandler**, solo que esta vez utilizamos el método **GetOne** de **Repository** y no olviden convertir el **id** a *uint* antes de pasarlo a la función. Si todo ha marchado bien, retornamos el usuario en formato [JSON](https://www.json.org/json-en.html).

#### Actualizar usuario

Ahora que ya podemos crear usuarios y obtener un usuario ya creado, vamos como actualizar un usuario existente.

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) UpdateHandler(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")

	id, err := strconv.Atoi(idStr)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	var u user.User
	err = json.NewDecoder(r.Body).Decode(&u)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	defer r.Body.Close()

	ctx := r.Context()
	err = ur.Repository.Update(ctx, uint(id), u)
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, nil)
}
```

Esta combina varias cosas que vimos anteriormente en **GetOneHandler** como obtener el **id** y convertirlo a *int*, con cosas que vimos en **CreateHandler** para [deserializar]((https://code.tutsplus.com/tutorials/json-serialization-with-golang--cms-30209)) [JSON](https://www.json.org/json-en.html) con ayuda del paquete [json](https://golang.org/pkg/encoding/json/).

Hecho esto, utilizamos el método **Update** de **Repository** para actualizar el usuario y si no existe algún error enviamos una respuesta con estado **200** (OK), en este caso sin **data**. También se podría considerar retornar el usuario actualizado, pero mantengamos esto sencillo.

#### Borrar un usuarios

Podemos crear, actualizar y leer usuario, eso es genial, pero aun no falta una operación para completar nuestro [CRUD](https://en.wikipedia.org/wiki/Create%2C_read%2C_update_and_delete), y esa es **borrar**.

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) DeleteHandler(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")

	id, err := strconv.Atoi(idStr)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	ctx := r.Context()
	err = ur.Repository.Delete(ctx, uint(id))
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, response.Map{})
}
```

Este *handler* es bastante sencillo y muy parecido a **GetOneHandler**. Solo obtenemos el **id** de la [URL](https://en.wikipedia.org/wiki/URL), lo convertimos a *int* y utilizamos el método **Delete** de **Repository** para eliminar el usuario con el correspondiente **id**. Finalmente enviando una respuesta [JSON](https://www.json.org/json-en.html) con código **200** (OK) y un objeto vacío para representar que se borro satisfactoriamente.

#### Definiendo rutas

Hasta ahora tenemos definida la lógica para manejar las peticiones pero aun no definimos que rutas deben ser manejadas por cada *handler*. Esto es bastante fácil con ayuda de [chi](https://github.com/go-chi/chi) y similares.

El donde y como definir las rutas puede variar, simplemente pueden ponerlo donde las parezca mas intuitivo, en este caso lo haremos de la siguiente manera:

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) Routes() http.Handler {
	r := chi.NewRouter()
	// TODO: add routes.

	return r
}
```

En este nuevo método, que llamamos **Routes**, vamos a definir las rutas especificas para el modelo **usuario**. Aprovechemos para aprender un poco sobre la manera en la que manejamos las rutas con [chi](https://github.com/go-chi/chi), comenzaremos con **GetAllHandler** de esta manera:

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) Routes() http.Handler {
	r := chi.NewRouter()
	
	r.Get("/", ur.GetAllHandler)
	return r
}
```

Como pueden ver, **r** que es una puntero al tipo **chi.Mux** tiene métodos que hacen referencia a los [métodos http](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) como **GET**, **POST**, **PUT**, **DELETE**, entre otros. De esta manera es muy sencillo vincular un *handler* con un [método http](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) especifico.

Gracias a lo anterior, podemos vincular el método **CreateHandler** al mismo patrón de ruta pero con un [método http](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) diferente, en este caso **POST**.

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) Routes() http.Handler {
	r := chi.NewRouter()

	r.Get("/", ur.GetAllHandler)

	r.Post("/", ur.CreateHandler)

	return r
}
```

De igual forma podemos agregar el resto de los *handlers*, pero debemos recordar que los tres restantes tomaban un parámetro por su [URL](https://en.wikipedia.org/wiki/URL) y aun no hemos visto como hacer eso. Así que tomemos como ejemplo el método **GetOneHandler**.

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) Routes() http.Handler {
	r := chi.NewRouter()

	r.Get("/", ur.GetAllHandler)

	r.Post("/", ur.CreateHandler)

	r.Get("/{id}", ur.GetOneHandler)

	return r
}
```

Como posiblemente supongan, con esas sintaxis de llaves ({}) podemos definir los parámetros en la [URL](https://en.wikipedia.org/wiki/URL), y el texto que se encuentra dentro de ellas sera el que usaremos como **clave** al momento de recuperar ese valor gracias a la función **URLParam** del paquete [chi](https://github.com/go-chi/chi), que en este caso es **id**. Ahora que sabemos esto, es bastante sencillo agregar los *handlers* restantes con sus respectivos métodos: **DELETE** para **DeleteHandler** y **PUT** para **UpdateHandler**.

```go
// internal/server/v1/user_router.go
// […]

func (ur *UserRouter) Routes() http.Handler {
	r := chi.NewRouter()

	r.Get("/", ur.GetAllHandler)

	r.Post("/", ur.CreateHandler)

	r.Get("/{id}", ur.GetOneHandler)

	r.Put("/{id}", ur.UpdateHandler)

	r.Delete("/{id}", ur.DeleteHandler)

	return r
}
```

### Rutas de publicaciones

Este proceso un bastante similar al que realizamos con los usuarios, así que avanzaremos mas rápido o resultaría muy repetitivo. Voy a compartir el código en cada caso y solo me detendré a explicar puntos que, a mi criterio, requieran mayor explicación.

> Si algo no les queda claro, siéntanse libres de dirigirse (con o sin antorchas 😬) hacia la caja de comentarios y les responderé tan pronto como me resulte posible.

Iniciamos creando un nuevo archivo dentro de paquete **v1** al cual llamaremos **post_router.go**

```bash
touch internal/server/v1/post_router.go
```

Y agregamos el siguiente contenido.

```go
// internal/server/v1/post_router.go
package v1

import (
	"github.com/orlmonteverde/go-postgres-microblog/pkg/post"
)

type PostRouter struct {
	Repository post.Repository
}
```

Hecho esto, podemos comenzar a agregar los métodos correspondientes.

#### Crear publicación

```go
// internal/server/v1/post_router.go
// […]

func (pr *PostRouter) CreateHandler(w http.ResponseWriter, r *http.Request) {
	var p post.Post
	err := json.NewDecoder(r.Body).Decode(&p)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	defer r.Body.Close()

	ctx := r.Context()
	err = pr.Repository.Create(ctx, &p)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	w.Header().Add("Location", fmt.Sprintf("%s%d", r.URL.String(), p.ID))
	response.JSON(w, r, http.StatusCreated, response.Map{"post": p})
}
```

#### Obtener publicaciones

```go
// internal/server/v1/post_router.go
// […]

func (pr *PostRouter) GetAllHandler(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	posts, err := pr.Repository.GetAll(ctx)
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, response.Map{"posts": posts})
}
```

#### Obtener una publicación

```go
// internal/server/v1/post_router.go
// […]

func (pr *PostRouter) GetOneHandler(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")

	id, err := strconv.Atoi(idStr)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	ctx := r.Context()
	p, err := pr.Repository.GetOne(ctx, uint(id))
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, response.Map{"post": p})
}
```

#### Actualizar publicación

```go
// internal/server/v1/post_router.go
// […]

func (pr *PostRouter) UpdateHandler(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")

	id, err := strconv.Atoi(idStr)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	var p post.Post
	err = json.NewDecoder(r.Body).Decode(&p)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	defer r.Body.Close()

	ctx := r.Context()
	err = pr.Repository.Update(ctx, uint(id), p)
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, nil)
}
```

#### Borrar publicación

```go
// internal/server/v1/post_router.go
// […]

func (pr *PostRouter) DeleteHandler(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")

	id, err := strconv.Atoi(idStr)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	ctx := r.Context()
	err = pr.Repository.Delete(ctx, uint(id))
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, response.Map{})
}
```

#### Obtener las publicaciones de un usuario

¿Recuerdan que comente antes que solo me detendría a explicar detalles que considerara diferentes a los ya explicados para **UserRouter**? Pues para que no parezca que solo estoy pegando código (eso estaba haciendo 🤫), explicare este *handler* un poco, aunque es bastante parecido a **GetOneHandler**.

```go
// internal/server/v1/post_router.go
// […]

func (pr *PostRouter) GetByUserHandler(w http.ResponseWriter, r *http.Request) {
	userIDStr := chi.URLParam(r, "userId")

	userID, err := strconv.Atoi(userIDStr)
	if err != nil {
		response.HTTPError(w, r, http.StatusBadRequest, err.Error())
		return
	}

	ctx := r.Context()
	posts, err := pr.Repository.GetByUser(ctx, uint(userID))
	if err != nil {
		response.HTTPError(w, r, http.StatusNotFound, err.Error())
		return
	}

	response.JSON(w, r, http.StatusOK, response.Map{"posts": posts})
}
```

En este caso el parámetro que estamos extrayendo de la [URL](https://en.wikipedia.org/wiki/URL) no se llama **id**  sino **userId**, aunque esto es un detalle menor, podríamos llamarlo de la manera en la que consideremos mas adecuada siempre y cuando usemos la misma **clave** al definir la ruta. Realizamos la conversión de *string* a *int*, utilizamos el método **GetByUser** de **Repository** para obtener las publicaciones relacionadas a un usuario por su **id** y enviamos la respuesta de tipo [JSON](https://www.json.org/json-en.html).

#### Definiendo rutas

De manera similar al lo que realizamos con **UserRouter**, crearemos un método **Routes** que retorne un **http.Handler** dentro del cual definiremos nuestras rutas.

```go
// internal/server/v1/post_router.go
// […]

func (pr *PostRouter) Routes() http.Handler {
	r := chi.NewRouter()

	r.Get("/user/{userId}", pr.GetByUserHandler)

	r.Get("/", pr.GetAllHandler)

	r.Post("/", pr.CreateHandler)

	r.Get("/{id}", pr.GetOneHandler)

	r.Put("/{id}", pr.UpdateHandler)

	r.Delete("/{id}", pr.DeleteHandler)

	return r
}
```

## Conectando las rutas

Actualmente tenemos nuestras funciones **handler** y rutas definidas tanto para **usuarios** como para **publicaciones**, pero aunque levantemos el servidor no podremos realizar consultas a estas rutas porque aun no las conectamos a nuestro **Server**. Así que crearemos un nuevo archivo dentro del paquete **v1** al que llamaremos **api.go**.

 ```bash
touch internal/server/v1/api.go
```

Y agregamos el siguiente contenido

```go
// internal/server/v1/api.go
package v1

import (
	"net/http"

	"github.com/go-chi/chi"
)

func New() http.Handler {
	r := chi.NewRouter()

	return r
}
```

En este punto vamos a aprovechar la posibilidad de agrupar rutas que nos proporciona [chi](github.com/go-chi/chi), pero primero debemos definir una nueva variable de tipo puntero a **UserRouter**.

```go
// internal/server/v1/api.go
// […]

func New() http.Handler {
	r := chi.NewRouter()

	ur := &UserRouter{
		Repository: &data.UserRepository{
			Data: data.New(),
		},
	}

	return r
}
```

Nuestra nueva variable **ur** es un puntero a **UserRouter** en el que estamos inicializando su campo **Repository** con un puntero al tipo **data.UserRepository** que desarrollamos en la [parte 3](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-3-29eb), el cual, a su vez, contiene un campo llamado **Data** que es un puntero al tipo **data.Data** y lo estamos inicializando gracias a la función **New** del paquete **data** que creamos en la [parte 2](https://dev.to/orlmonteverde/api-rest-con-go-golang-y-postgresql-parte-2-137l).


```go
// internal/server/v1/api.go
// […]

func New() http.Handler {
	r := chi.NewRouter()

	ur := &UserRouter{Repository:
		&data.UserRepository{Data: data.New()},
	}

	r.Mount("/users", ur.Routes())

	return r
}
```

Con nuestra variable **ur** preparada podemos utilizar la el método **Mount** del **Router** podemos anidar las rutas definidas dentro del **http.Handler** que retorna el método **Routes** de nuestro **UserRouter**. Dicho de otra forma, las rutas que definimos dentro de **Routes** ahora tendrán como base */users*, por ejemplo, para el caso de `r.Get("/", ur.GetAllHandler)`, la ruta sera */users/*. Quedara mas claro en un momento cuando realicemos peticiones de prueba a nuestra **API**.

```go
// internal/server/v1/api.go
// […]

func New() http.Handler {
	r := chi.NewRouter()

	// [...]

	pr := &PostRouter{
		Repository: &data.PostRepository{
			Data: data.New(),
		},
	}

	r.Mount("/posts", pr.Routes())

	return r
}
```

De manera similar agregamos nuestras rutas para publicaciones, en este caso bajo la ruta */posts*. Hecho esto, vamos a nuestro archivo **server.go** en el paquete **server** (internal/server/server.go) y agregamos las rutas de nuestra **API** versión uno.

```go
// internal/server/server.go
package server

// [...]

// New inicialize a new server with configuration.
func New(port string) (*Server, error) {
	r := chi.NewRouter()

	// API routes version 1.
	r.Mount("/api/v1", v1.New())

	serv := &http.Server{
		Addr:         ":" + port,
		Handler:      r,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	server := Server{server: serv}

	return &server, nil
}

// [...]
```

Ahora ya tenemos nuestras rutas correctamente configuradas 😀. Vamos levantar el servidor y hacer pruebas.

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

Si ahora visitamos desde nuestro navegador de confianza la dirección *http://localhost:9000/api/v1/users/* deberían obtener una salida similar a la siguiente.

```json
{"users":null}
```

De igual forma si visitamos *http://localhost:9000/api/v1/posts/*.

```json
{"posts":null}
```

Tenia la intención de realizar las pruebas a todos los *endpoints* con [curl](https://curl.haxx.se/) y/o [postman](https://www.postman.com/), pero esta parte se ha extendido bastante, así que lo estaremos abordando en la siguiente junto con [JSON Web Tokens](https://jwt.io/).

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.3.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)