In previous tutorial we created a simple REST API in Go using the Gin framework. We covered the basics of setting up a project, creating routes, and handling requests.

In this tutorial, we will do some refactoring and add more features to our API.

Add error handling

In the previous tutorial, we didn’t handle errors properly. We need to add error handling to our API to make it more robust.

If we look at our main function, we might have a single place where we are not handling errors - returned by the Run method of the gin.Engine:

1func main() {
2	r := gin.Default()
3	r.GET("/tasks", getTasks)
4	r.POST("/tasks", createTask)
5	r.PUT("/tasks/:id", updateTask)
6	r.DELETE("/tasks/:id", deleteTask)
7
8	r.Run(":8080")
9}

How we know this? You can take a look at the documentation, library source code, or even hover over the method in your IDE to see the documentation. Morover, modern IDEs will show you a warning if you are not handling the error or if there is something wrong with the code.

In order to handle the error, we need to check the return value of the Run method and exit the project if there is an error. We can use the log.Fatal function to log the error and exit the program or just panic:

 1func main() {
 2	r := gin.Default()
 3	r.GET("/tasks", getTasks)
 4	r.POST("/tasks", createTask)
 5	r.PUT("/tasks/:id", updateTask)
 6	r.DELETE("/tasks/:id", deleteTask)
 7
 8	if err := r.Run(":8080"); err != nil {
 9		panic(err)
10	}
11}

.env file

It’s a good practice to store configuration in environment variables. It’s a good idea to use environment variables for things like the port number, database connection string, API keys, or any other sensitive information.

Since we are not using a database in this tutorial, we will use the port number as an example. And for this we will use godotenv package.

Let’s start by creating a .env file in the root of our project with the following content:

1PORT=8080

This will allow us to change the port number without modifying the code.

Next, we need to install the godotenv package. This can be done using the command: go get github.com/joho/godotenv.

After that, we need to load the environment variables from the .env file in our main function. This should be done before creating the gin.Engine:

 1func main() {
 2	if err := godotenv.Load(".env"); err != nil {
 3		panic(fmt.Sprintf("Error loading .env file: %v", err))
 4	}
 5
 6  port := os.Getenv("PORT")
 7  if port == "" {
 8		panic("PORT is not set in .env file")
 9	}
10
11	// ...
12}

Note, that we are using os.Getenv to get the value of the PORT environment variable here, so a new import is needed: import "os".

What this new code does is load the environment variables from the .env file and check if the PORT variable is set. If it’s not set, the program will panic with a custom error message - there we are bringing back fmt package.

The only thing left is to utilize the port variable. To do so, we need to replace the hardcoded port number in the main function with the port variable. However, since we are making refactoring and other improvements in this tutorial, let’s make our code more readable and move the server setup to a separate variable and then use it in the r.Run method:

 1func main() {
 2	if err := godotenv.Load(".env"); err != nil {
 3		panic(fmt.Sprintf("Error loading .env file: %v", err))
 4	}
 5
 6	port := os.Getenv("PORT")
 7	if port == "" {
 8		panic("PORT is not set in .env file")
 9	}
10
11	r := gin.Default()
12	r.GET("/tasks", getTasks)
13	r.POST("/tasks", createTask)
14	r.PUT("/tasks/:id", updateTask)
15	r.DELETE("/tasks/:id", deleteTask)
16
17	serverUrl := fmt.Sprintf(":%s", port)
18	if err := r.Run(serverUrl); err != nil {
19		panic(err)
20	}
21}

Now, if you run the project, it should work as before, but now you can change the port number in the .env file to whatever you want.

Conclusion

In this tutorial, we added error handling to our API and used environment variables to store configuration. This makes our API more robust and easier to configure. Stay tuned for more!