API, Golang, REST

Creating an API with Golang Gin Framework

Recently I made a blog post on how to create an API with only the Golang standard library. After doing that I thought it would be nice to make a more practical API with a Golang framework, I will be creating a to-do list API using Gin. All the code below, and more which didn't make it into this post will be here Over the next few weeks I will be trying out all the hot frameworks and create a sample project in each, so subscribe to my mailing list if you want to be notified about those!


I decided to give Gin a shot because of how minimal it is, other frameworks like Revel have so much to it, I wanted to start with something less opinionated and more lightweight. Although it is lightweight, it has all the features you need when creating an API such as middleware, route grouping, panic recovery, and json validation.

Getting Started

The first step which I like to take when starting a new API in Golang is to define how the main.go should look. I always find this to be a natural place to start, and really clarify what needs to be built.

A large part of the setup is already handled since we're using Gin but there is still a bit to setup. All we really need to do is:
1. Initialize Gin
2. Setup our database connection
3. Initialize our controller
4. Bind routes
5. Run the server

db, err := database.Connect(os.Getenv("PGUSER"), os.Getenv("PGPASS"), os.Getenv("PGDB"), os.Getenv("PGHOST"), os.Getenv("PGPORT"))
if err != nil {
    log.Fatal("err", err)
todoController := controllers.NewTodoController(db)
r := gin.Default()
routes.CreateRoutes(r, todoController)

It should look something like this! Now that we have defined our main.go file lets move on to the interesting stuff.

Database Connection

I created a small database connection function to keep the main.go file clean. I placed the file in utils/database/database.go which you can find here.

connStr := fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%s",
        user, password, dbname, host, port)
return sql.Open("postgres", connStr)

Todo Item Model

Before we start with the actual controller, we should figure out what our todo list item should look like. Most to do lists allow for items to have a title and description. You can also set them to be completed or not. Create models/item.go and make a struct like:

type Item struct {
    ID          int    `json:"id"`
    Title       string `json:"title"`
    Description string `json:"description"`
    Completed   bool   `json:"status"`

The json:"id" is important for encoding items into JSON responses. It allows the object to be serialized in a friendly format.

Todo Controller

Okay, so now we need to create our todo controller. If we look back at main.go you will see we need a function NewTodoController(db) so lets start with that. All the function will do is return a pointer to a TodoController.

func NewTodoController(db *sql.DB) *TodoController {
    return &TodoController{DB: db}

type TodoController struct {
    DB *sql.DB

Notice the TodoController has a database, that is so we can access the database from inside our controllers via dependency injection.

Now let's figure out what we want functions we want the TodoController to have. To make a todo list, you need a couple things:

  1. Create items
  2. Get items
  3. Update items
  4. Delete items
  5. Get individual item
    So we need functions for each of these. I will not write them all out in this post, but to check out the other functions click here.

Creating the get items function is worth writing out here because it is the most complicated function, and it is located at controllers/todo_controller.go. There are a couple things to think about before you start this function. Most to-do lists allow you to see all your items or just the outstanding items. I believe that is an important feature to add, so let's do that.

The first step when building this file out is to have a get parameter to specify if you want all the items or just the ones which aren't completed.

func (tc *TodoController) Items(c *gin.Context) {
    allStr := c.Query("all")
    all, err := strconv.ParseBool(allStr)
    if err != nil {
        all = true

Once that is retrieved and validated, we want to get the items. We will make a repository in the next step, but for the time being, we'll pretend we have a repository with the GetItems function. If there is an error when retrieving the items, we will log it, and return an empty internal server error. Gin has a couple functions for returning data to the client, you can pass strings, json, yaml, xml, etc... But for this project, I am just using strings and json. Strings are just used for empty responses (404, 500 status codes).

    items, err := repositories.GetItems(tc.DB, all)
    if err != nil {
        log.Println("err", err)
        c.String(http.StatusInternalServerError, "")

The last step is to return the todo list items. Gin makes this super easy with their JSON function. All we need to do is pass in the HTTP status code and an object for them to serialize. Under the hood they use the encoding/json package.

    c.JSON(http.StatusOK, items)


In the controller we made an assumption about a repository, we are going to create that now. The file is located repositories/todo_repository.go. There are many more functions on the Github project, here.

First, we have to initialize some variables we will use later in the function and write the query. All we're doing in this query is grabbing the data for our item model. There is no pagination here, but it would be simple to add.

func GetItems(db *sql.DB, all bool) ([]*models.Item, error) {
    var rows *sql.Rows
    var err error
    query := `

Next, we need to make an if else statement for the case that they only want outstanding items. If they don't, we just get all the items in the database.

    if !all {
        query += "where completed = $1"
        rows, err = db.Query(query, all)
    } else {
        rows, err = db.Query(query)

    if err != nil {
        return nil, err

The final step in the function is to create the item list. We make an empty slice of items and then append an item for each row in the database. If there are any errors along the way, we return the error.

    items := make([]*models.Item, 0)

    for rows.Next() {
        var item models.Item
        err = rows.Scan(&item.ID, &item.Title, &item.Description, &item.Completed)
        if err != nil {
            return nil, err
        items = append(items, &item)
    return items, err


The final and most important thing we need to do to finish off this API is to bind the controller to the API routes. Luckily, Gin makes this super easy. All we do is create a routes/routes.go file and bind them.

func CreateRoutes(r *gin.Engine, tc *controllers.TodoController) {
    r.GET("/items", tc.Items)
    r.POST("/item", tc.Create)
    r.GET("/item/:itemID", tc.Get)
    r.PUT("/item/:itemID", tc.Update)
    r.DELETE("/item/:itemID", tc.Delete)

Final Thoughts

This is my first exposure to the Gin framework, using it was pleasant, and it is a nice, intuitive, lightweight alternative to the standard library. I would definitely consider using this over the standard library due to the fact it cuts down the amount of code you write significantly. I'm not sure if it is better than using the gorilla toolkit but for those who just "want it to work," while not feeling like a massive framework, this is for you.

Author image

About Ryan McCue

Hi, my name is Ryan! I am a Software Developer with experience in many web frameworks and libraries including NodeJS, Django, Golang, and Laravel.