02 - Creating Route Handlers In Gin
Tags: go, gin, tutorial, docker
Published: 2020-10-03 03:25:59

In the [previous tutorial](https://dev.to/jacobsngoodwin/full-stack-memory-app-01-setup-go-server-with-reload-in-docker-62n) we set up a Go application called _account_ with automatic reloading behind a Traefik reverse proxy, all running in a Docker development environment.

In this tutorial we'll set up http routes using gin, which will allow us to create API endpoints for managing a user's account. 

By the time we're done, your file structure should be as follows.

![Final File Structure](https://dev-to-uploads.s3.amazonaws.com/i/24lfsz70jadeoveqf3dm.PNG)

FYI - I've created a git branch for each tutorial in the [Github repository](https://github.com/JacobSNGoodwin/memrizr). You can use the [branch for tutorial 01](https://github.com/JacobSNGoodwin/memrizr/tree/lesson-01) to start off with the same code I'm using today! To start the app, run `docker-compose up` inside of the top-level directory.

If you prefer, check out the video tutorial as well!

{% youtube 1eLdjW7tMr4 %}

### Adding _Handler_ Package

To get started, let's create a new folder inside of _~/account_ called _handler_. Inside of this folder, create a file called _handler.go_. This file will be used to initialize the handler package. My convention in this project will be to name the the "main" file for each package with the name of the package.

We do the following in this file. 

1. Create a Handler struct. This struct will ultimately hold "services" properties. These services contain methods that implement the application's core functionality.   
2. We create a Config struct. This config will hold all values needed to initialize this _handler_ package. 
3. A NewHandler "factory" function. This function is used to initialize the routes and will be called from the _main_ package.

I will be using this factory with configuration approach throughout the project as it helps make clear which dependencies are required for each package. And while you will often see this in other Go projects, it is by no means standard. One can directly initialize a struct instead. 

We will pass a reference to the gin router created inside of `package main` into the _Config_ for our _handler_ package. With this reference, we can create a route group which allows us to set a common "base URL" ("/api/account") for our endpoints.

On this route group, we'll create a get route which should return basic JSON. This GET route has been copied and pasted from the route we originally placed inside of main.go.


```go
package handler

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// Handler struct holds required services for handler to function
type Handler struct{}

// Config will hold services that will eventually be injected into this
// handler layer on handler initialization
type Config struct {
	R *gin.Engine
}

// NewHandler initializes the handler with required injected services along with http routes
// Does not return as it deals directly with a reference to the gin Engine
func NewHandler(c *Config) {
	// Create a handler (which will later have injected services)
  h := &Handler{} // currently has no properties
  
	// Create an account group
	g := c.R.Group("/api/account")

	g.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"hello": "space persons",
		})
	})
}
```

Before we test this code, make sure to remove the GET route handler from `package main` and replace it with the initialization of the newly created `package handler`. Note the NewHandler factory merely modifies the gin router and does not return anything.

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

handler.NewHandler(&handler.Config{
  R: router,
})

srv := &http.Server{
  Addr:    ":8080",
  Handler: router,
}
```
### Creating Application Routes

Inside _handler.go_, we'll add all of the routes inside of the _NewHandler_ function along with corresponding handler methods for each route. We'll eventually move the handler functions to separate files as the application grows.

You can see that each handler method returns some simple json with "hello" as the key, and a value telling which handler has been called. 

```go
func NewHandler(c *Config) {
	// Create an account group
	// Create a handler (which will later have injected services)
	h := &Handler{} // currently has no properties

	// Create a group, or base url for all routes
	g := c.R.Group("/api/account")

	g.GET("/me", h.Me)
	g.POST("/signup", h.Signup)
	g.POST("/signin", h.Signin)
	g.POST("/signout", h.Signout)
	g.POST("/tokens", h.Tokens)
	g.POST("/image", h.Image)
	g.DELETE("/image", h.DeleteImage)
	g.PUT("/details", h.Details)
}

// Me handler calls services for getting
// a user's details
func (h *Handler) Me(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"hello": "it's me",
	})
}

// Signup handler
func (h *Handler) Signup(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"hello": "it's signup",
	})
}

// Signin handler
func (h *Handler) Signin(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"hello": "it's signin",
	})
}

// Signout handler
func (h *Handler) Signout(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"hello": "it's signout",
	})
}

// Tokens handler
func (h *Handler) Tokens(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"hello": "it's tokens",
	})
}

// Image handler
func (h *Handler) Image(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"hello": "it's image",
	})
}

// DeleteImage handler
func (h *Handler) DeleteImage(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"hello": "it's deleteImage",
	})
}

// Details handler
func (h *Handler) Details(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"hello": "it's details",
	})
}
```

With your docker-compose up running, you should now be able to make request to the above endpoints using the client of your choice. I use a lot of Postman, but here's a quick example with curl from the command line.  

```bash
➜  curl -X GET http://malcorp.test/api/account/me
{"hello":"it's me"}%
➜  curl -X POST http://malcorp.test/api/account/signin
{"hello":"it's signin"}%
```

### Configure Base URL with Environment Variable

We're going to add the base url `/api/account` to an environment variable. We'll do this now so that setting up environment variables later on will be a breeze!

To set project-wide environment variables, let's create a ".env.dev" file in the root _memrizr_ directory.

```bash
ACCOUNT_API_URL=/api/account
```

I also recommend creating a _.gitignore_ file and adding ".env.dev" as an entry so that this file is omitted from your repository (eventually we'll add some credentials you don't want ending up online).

We can then inject this environment variable into the _account_ service defined in the _docker-compose.yml_ file as follows. Now the account application will have access to any environment variable added to _.env.dev_! This is just one way to create environment variables in Docker or Docker Compose. 

```yaml
 account:
    ...
    ...
    env_file: .env.dev
```

Finally, we need to tell the application to use this environment variable inside of _handler.go_. To make this work, update the name of argument of _Group_ to get the environment variable we just setup. 

```go
// Create a group, or base url for all routes
	g := c.R.Group(os.Getenv("ACCOUNT_API_URL"))
```

Now you can restart the application with `docker-compose up`, and re-run the above curl commands (or in the client of your choice).

### Next Time

Now that we've created routes, we have an entry point from the outside world into our application. This prepares us to discuss the application architecture and to being building out the layers of the application.

¡Hasta el próximo tutorial!