Photo by Adel on Unsplash

Following the idea of the previous post, we will get the project we have started and will add now a database connection, and a user CRUD API. It will be really simple, with simple validations, and simple error handling, but I think it will give you some ideas about how the modules and files are isolated, and how to handle HTTP and database operations, read config from environment variables, and also we will use a docker-compose to provide our database service. Let’s get started!

Our first package — config

This internal package is special because it is only visible to the same root based packages that it is created. We will use it as a container for our first internal package called config and inside this folder we will create our file config.go After that, our project will look like:

package current structure

This file will contain our application configuration. Based on the 12factor, configuration should be read from environment, and to do that we will create a struct that is the equivalent of a class in most of the languages. Our Config will have fields to expose our ConnectionString and also the environment in which our application is running.

In the first line we define our package, naming it config. In the line 3, we import the standard package called os that we will use to read the configuration from the OS environment variables.

From line 5 to 8 we are declaring our Config struct. We write it with ts first letter capitalized (Pascal Case), because in Go, the uppercase entities are public outside the package, and the lowercase are private. We will define two fields in it right now, Environment that will be our application running environment (local, test or production) and ConnectionString that will have our database connection string. Both fields are also declared uppercase to be visible outside the config package.

From line 10 until 16 we are creating a function called getConfigValue to allow us to read values from the environment, but returning a default value if the configuration doesn’t exist. We use the os package’s function LookupEnv that accepts one argument and returns two values. The argument is the environment variable name, and it returns the variable value as the first result, and a boolean value which is true when the variable is found. When the variable is found, we return the variable value and if it is not found, we return the default value received value.

Creating our first constructor

The database package

Just like the when we were creating the config package, we are defining the package name as database, and doing the needed imports. In this one we are importing our config package, the sqlx package that we will use to connect to the database, and the MySQL driver.

It is important to note that we are using an alias (_) to the MySQL driver package because in Go, if we don’t use an imported package it will show a compilation error, and using the _ as the alias, we are telling the compiler to ignore the given import. We need to import it to add support to MySQL to the default database package (more info).

We will use the sqlx package, that has the same methods as the default database/sql but implements some other methods that makes it easier to write map queries and updates with structs than the default package methods, we will get more into it a little later.

The constructor this time is called NewDB and has an argument of the type of our recently created Config struct. You can also note that in the method Open, that is called on line 29, we pass the driverName (“mysql”) as the first argument, and our ConnectionString from the received config as the second one. This method returns an interface that represents a pool of connections to the database, so we don’t neet to implement anything to handle concurrent uses with it.

Our first domain level package — user

We will name our package as user and will import a package called validator to help us validate our inputs in an easier way. After that, is created the User struct with some basic fields just to show the functionality of a simple CRUD operations using chi.

Adding validation to our struct

The validator package uses the validate tag to show which fields should be validated, and their validation rule. In line 9, when we define the min=3 we are saying that the minimum length for our field is 3, when the field is a number, it validates the value instead of the length. In the function Validate declared on line 13, we are using an instance of the validator package to validate our struct passed as argument, and return an error if the validation fails.

Creating the repository

There are a lot of imports this time, but let’s just go it over in parts. First we declare our UserRepository struct with only one field db that is lowercase this time, as we don’t need to expose it as public outside our package.

After that we will declare some constants with the SQL commands we will need to run on database to provide the CRUD operations for the user entity. The first method is the CreateUser that receives a User struct as argument and returns a point to a user (to return the inserted id) and an error in case we cannot create the user. This is the biggest method we have, so let’s get into it so we can understand it better.

If you are not yet used to how Go knows which methods are from the package and which ones are related to structs, just note that after the func reserved keyword, if we open a parenthesis and create a parameter with a type, just like in the func (u *UserRepository) CreateUser, the method is being attached to that struct, being a member of it, instead of the package.

Inserting a user

The bcrypto method put a random salt and cryptography a text using it, making it possible to evaluate if the password is correct, only having the right password and the stored salt, giving an extra layer of security to the stored sensible data.

After validating if the hashAndSalt didn’t return an error, we just need to run our insert into the database. For that, we are using the sqlx method called NamedExec that is a simple execute to database, but that parses our parameters list from the command, and map them to the struct fields we passed in the second argument, in a way we don’t need to use positional arguments, and pass a lot of arguments to the exec.

Another error validation and voilà we have a function user insertion on database, and just return the inserted user with the id, and nil to show that no error occurred. Now that we already have a user into the database, lets see how to read it.

Retrieving a user from database

The Get method returns an error when it fails, so we don’t need to add a lot of error handling here, just returning the user struct we defined before, and the error that will be nil if the methods execute correctly, on an error object when it fails, just as our method.

Updating user

To do this, our update SQL command doesn’t have the fields in the command, and we implement a variadic method called updateUser in lowercase, to avoid being exposed outside the package, that receives a user struct, that has the data we are going to update, and the variadic argument, that is any number of arguments, that will be received as a slice, that makes it easier to be invoked without the developer initiate a slice to pass as argument, as we can see in line 67. Now we just need to call the internal updateUser in our public Update method.

Deleting a user

Exposing the endpoints

As we already got into package and import a lot of time, from this point on, and in the next articles, I will get straight to implementation, unless there is something especial in the package or import sections.

Creating a UserHandler

It is important to notice that we are not exposing our userHandler and its constructor outside of the package, as both of them are lowercase, but we expose the RegisterRoutes method that will register the routes in a given router, keeping the endpoint registration encapsulated inside the package.

As this article is getting a lot bigger than I expected, I will go a little faster here. We are exposing four endpoints, one for each CRUD operations base on REST HTTP verbs, we are not exposing a GET for the list of users right now. To do this, on line 99 we create a route group /user and pass a function in the second argument that receiver a router that is related to this group. In this function we relate each handler function to its endpoint.

One point that is important to show is that we use an {id} in the route definition, and this is the name of the param we will retrieve inside the methods using chi.URLParam(r, "id") that returns the value of the given param or an empty string when not found.

To read the data in createUser and updateUser we’ve created a method called getUserFromRequest that reads the data from the request body using json.Decoder struct and parses to a User variable using the Decode method.

To return errors I’m using the http.Error method and returning the errors as plain text for now (we will improve it later) just for simplicity.

Registering handler in our app (almost over)

To highlight the changes, we added a middleware from chi on line 16 just to get a logger to output the received requests and the time it took to respond and also added in the line 27, the calls the user.RegisterRoutes that we register all our user handlers.

(Finally) Running our app

Console output

Conclusion

I would also like to thank a lot to my colleagues Erick Oliveira, Niccolas Costa, João Miguel Noronha e Diego Alves for reviewing and giving me feedback about it.

I hope you liked the reading, and would love to get your feedback about this article or the series. See you all in the next article.

Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Sign up for Best Stories

By Dev Genius

The best stories sent monthly to your email. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Felipe Caputo

Written by

Software Engineer @ Mercado Livre, Father of 3 amazing children/teens and someone who loves technology

Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store