Using Render and Go for the First Time

John Vester - Jan 31 '22 - - Dev Community

Article Image

My ability to remember things can drive my wife crazy. Earlier this week, she asked me two questions after I finished telling her a story that I had recalled from my long-term memory:

  • How do you remember such things?
  • Why do you even remember that memory?

Honestly, I have no clue how to answer her questions, only that I recognized this ability earlier in my life. We both agreed that if there was a “useless trivia show,” I would be the world champion … hands down.

I feel like some events that I experience for the first time are often indexed in the long-term chambers of my brain, later becoming the source of my stories that I feel compelled to share with her. In terms of career implications, it does not take much effort to recall the first time I worked in a programming language or started using a service or framework.

Reading about the service provided by Render — one that promises “Zero DevOps” — made me want to take it for a test drive. Looking over the list of supported languages, I saw an opportunity to create a service in a programming language that is new to me.

As a result, I decided to give the Go programming language a shot, in hopes of adding a new deposit to my long-term memory bank.

The Magic 8-Ball Service

To make things fun, consider an example where Mattel asks for the creation of a RESTful API that mimics the Magic 8-Ball toy that was invented in 1946:

Magic 8-Ball

For those who are not aware, the Magic 8-Ball is a novelty item that performs basic fortune-telling tasks. With the 8-Ball image facing up, the person holding the ball asks a yes-no question, then turns it over to reveal an answer. The reply magically floats to the top out of the dark black ocean inside the toy.

Magic 8-Ball Response

The original design contained 20 unique answers, and the acceptance criteria is that only these responses will be allowed in the RESTful API:

Original Magic 8-Ball Answers

Ten positive answers are noted with green bullets, and the remaining ten are an equal combination of non-committal (yellow) and negative (red) answers.

Consumers of the API simply need to make a standard GET request, which will return a random answer from the list of possible options. For validation purposes, Mattel would also like a second endpoint to retrieve a list of all available answers from this new service.

With my design, we can meet these requirements with the following endpoints:

  • GET /answer - randomly returns one of the twenty available answers
  • GET /answers - returns a list of all twenty available answers

Getting Started With Go

While visiting the download page on the Go site, I noticed the installation process was very similar to getting started with the Java programming language. The installer used a wizard-like design where I basically just had to click buttons to navigate through the process. Once finished, I used the following terminal command to verify a successful installation:

╭─me@johnjvester ~ 
╰─$ go version
go version go1.17.6 darwin/amd64
Enter fullscreen mode Exit fullscreen mode

Using IntelliJ IDEA, I added the Go plug-in and started a new project using the File | New | Project … command:

New IDEA Project - Screen 1

The project name of magic-eight-ball just seemed right for this endeavor:

New IDEA Project - Screen 2

After some research, I found that the Gin web framework provides the functionality needed for a RESTful API written in Go, so I installed it with the following command:

$ go get -u github.com/gin-gonic/gin
Enter fullscreen mode Exit fullscreen mode

To reference Gin inside my source code, I simply needed to include the following import:

import "github.com/gin-gonic/gin"
Enter fullscreen mode Exit fullscreen mode

Now we are ready to start building the Magic 8-Ball API.

Creating the Magic 8-Ball API

The first thing I want to do is establish the data side of the API. For this example, the following JSON structure will make up the response body when an answer is provided:

{
  "id" : number,
  "response" : string
}
Enter fullscreen mode Exit fullscreen mode

In Go, this JSON payload is defined (as shown below) and added to the main.go file:

type response struct {
    ID       int    `json:"id"`
    Response string `json:"response"`
}
Enter fullscreen mode Exit fullscreen mode

Since all of the data is static, I added the standard answers to the main.go file:

var responses = []response{
    {ID: 1, Response: "It is certain."},
    {ID: 2, Response: "It is decidedly so."},
    {ID: 3, Response: "Without a doubt."},
    {ID: 4, Response: "Yes definitely."},
    {ID: 5, Response: "You may rely on it."},
    {ID: 6, Response: "As I see it, yes."},
    {ID: 7, Response: "Most likely."},
    {ID: 8, Response: "Outlook good."},
    {ID: 9, Response: "Yes."},
    {ID: 10, Response: "Signs point to yes."},
    {ID: 11, Response: "Reply hazy, try again."},
    {ID: 12, Response: "Ask again later."},
    {ID: 13, Response: "Better not tell you now."},
    {ID: 14, Response: "Cannot predict now."},
    {ID: 15, Response: "Concentrate and ask again."},
    {ID: 16, Response: "Don't count on it."},
    {ID: 17, Response: "My reply is no."},
    {ID: 18, Response: "My sources say no."},
    {ID: 19, Response: "Outlook not so good."},
    {ID: 20, Response: "Very doubtful."},
}
Enter fullscreen mode Exit fullscreen mode

In order to leverage the responses variable, I added two functions to the main.go file:

func getAllAnswers(c *gin.Context) {
    c.IndentedJSON(http.StatusOK, responses)
}

func getRandomAnswer(c *gin.Context) {
    rand.Seed(time.Now().Unix())
    c.IndentedJSON(http.StatusOK, responses[rand.Intn(len(responses))])
}
Enter fullscreen mode Exit fullscreen mode

The getAllAnswers() function simply returns the responses list in JSON format, and the getRandomAnswer() function randomly picks one of the twenty options and returns the value in JSON format.

Finally, the main() function sets up the endpoints and the port used by the service:

func main() {
    router := gin.Default()
    router.GET("/answers", getAllAnswers)
    router.GET("/answer", getRandomAnswer)

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    if err := router.Run(":" + port); err != nil {
        log.Panicf("error: %s", err)
    }
}
Enter fullscreen mode Exit fullscreen mode

That’s all that is required to meet the acceptance criteria established by Mattel.

To start the application, we run the following command from the terminal:

╭─me@johnjvester ~/projects/jvc/go/magic-eight-ball 
╰─$ go run .
Enter fullscreen mode Exit fullscreen mode

The logs that displayed included those noted below:

[GIN-debug] GET    /answers        --> main.getAllAnswers (3 handlers)
[GIN-debug] GET    /answer         --> main.getRandomAnswer (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
Enter fullscreen mode Exit fullscreen mode

I can test the API by sending curl requests with the following command:

╭─me@johnjvester ~ 
╰─$ curl http://localhost:8080/answer
Enter fullscreen mode Exit fullscreen mode

This is the response I received:

{
    "id": 6,
    "response": "As I see it, yes."
}% 
Enter fullscreen mode Exit fullscreen mode

With the Go service running without any issues, I uploaded the code as a new project on GitLab:

https://gitlab.com/johnjvester/magic-eight-ball

Now I’m ready to see how Render works.

Deploying to Render Via GitLab

Getting started with Render was quick, easy, and free. I simply opted to use my GitLab credentials, which made things easy when selecting projects to deploy.

To get started, I selected the New button on the Render dashboard and chose the Web Service option:

Render - Web Service Option

I selected my magic-eight-ball project on GitLab:

Render - Select from GitLab

Within the Render platform, I decided to call my service jvc-magic-eight-ball and kept the defaults for all of the other options:

Render - Name the Service

Scrolling down, I opted to keep this service running on the free plan:

Render - Plan Options

Then, I clicked the Create Web Service button. Within a few minutes, the Render dashboard showed me that my service was up and running:

Render - Dashboard

I sent a request to the URL for my service on Render (https://jvc-magic-eight-ball.onrender.com/answer), and I received the following response:

{
    "id": 5,
    "response": "You may rely on it."
}
Enter fullscreen mode Exit fullscreen mode

Within just a few clicks, I deployed my first RESTful API written in Go to the Render platform—and this didn’t incur any costs. More importantly, the Render approach did not require any DevOps skills or knowledge for me to complete my task.

Conclusion

The introduction of this article talked about my ability to retain and recall historical events several years (if not decades) later. The fact that this entire experiment took less than an hour of my time—from start to finish—is certainly worthy of becoming another one of those events.

Starting in 2021, I have been trying to live by the following mission statement, which I feel can apply to any IT professional:

“Focus your time on delivering features/functionality which extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.”

  • J. Vester

In this article, not only was I able to produce a functional API quickly with very little source code, but I was also able to deploy the project to a cloud provider simply by performing a series of small steps. Zero DevOps was promised, and Zero DevOps was delivered. This allowed me to focus my time on providing features to my customers.

If you are interested in the full source code, just navigate over to GitLab:

https://gitlab.com/johnjvester/magic-eight-ball

Knowing that I currently have APIs and applications running in the Heroku ecosystem, I plan to start looking into what a conversion from Heroku to Render looks like in the next few weeks. So far, I am quite impressed with what Render has to offer.

Have a really great day!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .