A preview environment in less than 5 minutes for Gin (Go) and Postgresql
Tags: webdev, go, postgres, programming
Published: 2022-02-02 16:48:32

## Introduction

One does not simply deploy a production environment and a developing one on its favorite Cloud Provider (AWS, Scaleway, Digital Ocean). 

When it comes to testing new features or improvements, few options come to you. You do some unit tests but you’re not 100% sure everything works together. So you switch to integration tests but now you can’t be sure all your services will still work together. Then you set up an end-to-end testing pipeline but where do you run it ? On your developing branch once you merge your changes ? And what ? You rollback to a previous commit if it fails ? It’s time consuming and can introduce issues if someone rebases your flaky code.

And what if I tell you that there is a way to run your end-to-end tests on a dedicated environment deployed in **less than 5 minutes** ?

Qovery offers a way to do it blazingly fast. This platform does not only allow you to deploy your infrastructure and applications on your cloud account but also some great features to improve your production and preview environments is one of them.

## Demo

1. **Create a service with Gin**
    For this demo, I’ll create a little service that will allow me to store numbers in a database thru an API.

    ```go
func AddNumber(context *gin.Context, db *pg.DB) {
	value, parseErr := strconv.ParseInt(context.Param("number"), 10, 64)
	if parseErr != nil {
		context.String(http.StatusBadRequest, "%s", parseErr.Error())
		return
	}

	_, insertErr := db.Model(&Number{Value: value}).Insert()
	if insertErr != nil {
		context.String(http.StatusInternalServerError, "Cannot store %d: %s", value, insertErr.Error())
		return
	}

	context.String(http.StatusOK, "%d stored.", value)
}
```

2. **Deploy the app with Qovery**
    There are two ways to handle the build phase with Qovery, Buildpacks, which will identify the programming language and generate an image, or Docker with a Dockerfile. I’ll use the second one since I’m used to it and it gives me more control.

    ```dockerfile
    FROM golang:1.17.1-buster as build

    ADD ./cmd /src/cmd
    ADD ./pkg /src/pkg
    ADD ./go.mod /src
    WORKDIR /src/cmd
    RUN go get && go build -o /gin_test.bin main.go

    FROM debian:buster-slim as run

    RUN apt-get update && apt-get install -y ca-certificates 
    && apt-get clean
    COPY --from=build /gin_test.bin /usr/bin/gin_test
    CMD ["/usr/bin/gin_test"]
```

3. **Setup your environment on Qovery**
    Assuming I already have an infra running, I’ll just create "gin_test" project. 

    ![create_project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wnsjaqruu36ks13m83st.png)

    Then a “production” environment.

    ![create_env](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7n4uu4awca5ay5xzovo8.png)

    And to finish, my application.

    ![create_app_1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yxc0u7rfsijxp1cedqtr.png)

    ![create_app_2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzpuv4u3azk1ivqrgxjj.png)

    Then I enable the “Preview Environment” option.

    ![enable_preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c9haam1lzd09e3fsrnz9.png)

    If I take a look at auto generated environment variables, Qovery offers me all I need to connect to Postgresql. 

    ![env_vars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qfgjz3d9dsiwq55rezf.png)

    Let’s put it in my code.

    ```go
func ConnectToDatabase() *pg.DB {
	addr := fmt.Sprintf("%s:%s",
		GetEnvironmentVariable("POSTGRESQL_HOST"),
		GetEnvironmentVariable("POSTGRESQL_PORT"),
	)
	options := &pg.Options{
		User:     GetEnvironmentVariable("POSTGRESQL_LOGIN"),
		Password: GetEnvironmentVariable("POSTGRESQL_PASSWORD"),
		Addr:     addr,
		Database: GetEnvironmentVariable("POSTGRESQL_DEFAULT_DATABASE_NAME"),
	}

	db := pg.Connect(options)
	if db == nil {
		log.Fatal("Failed to connect to database. Closing app.")
	}

	log.Print("Connected to database.")

	return db
}
```

    And deploy it all !

    ![deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69kw32p82tg5idq4pia0.png)

    And let’s see if it works.

    ![add_curl_1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8pk3f0akhkrwnw54942.png)

    Amazing !!!

4. **Add a feature and open a pull request**
    Well, now I can store numbers but I’d like to get the list of stored ones when reaching another endpoint.
    
    
    ```go
func ListNumbers(context *gin.Context, db *pg.DB) {
	var numbers []Number
	listErr := db.Model(&numbers).Select()
	if listErr != nil {
		context.String(http.StatusInternalServerError, "Cannot list numbers: %s", listErr.Error())
		return
	}

	context.String(http.StatusOK, "Numbers list: %v", numbers)
}
```

    My service is ready to test, let’s open a pull request.
    
    ![pull_request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozjgkrbxut4ilu7cs4de.png)

5. **Check your preview environment**
    And now, if we take a look at Qovery console, we have a new environment named with me pull request.
    
    ![multiple_envs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/scyye4dwu43mc0xwirqr.png)

    And I also already have a fresh new URL related to my preview environment.

    Let's add some numbers since in my preview environment it's a fresh new database with dedicated endpoint.

    ![add_curl_2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mp4wp9zlxrig8y06xo3x.png)

    I now want to see if my new awesome list feature works.

    ![list_curl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/soy8ho6lyxa7ncfy2n4b.png)

    I’m now able to run manual tests or end-to-end tests on this endpoint.

6. **Merge it all**
    Since everything looks to work perfectly, let’s merge this !
    
    ![merge](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uu814j0molqven1c9uyr.png)

    We can notice the preview environment is deleting and the production one is redeploying Qovery side.

    ![ui_merge](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4vdj50k9q816rdeshe2.png)

    And now I can list previously stored numbers.
 
    ![list_curl_2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2cv8sa6x9kjkvc3xfncu.png)

## Conclusion

In this article, we quickly see how to set up a backend service written in Go with Gin linked to a Postgresql database. We enabled the Preview Environment feature to develop new features more quickly. And BOOM ! Opened a pull request so I can test my feature !

Git repository : [https://github.com/MacLikorne/gin_test](https://github.com/MacLikorne/gin_test)