Create a REST API Using Swagger in Go Part 4

This is the final part of a four-part series on building an API using Swagger. If you haven’t read the first three parts, you can do so here:

  1. Part 1
  2. Part 2
  3. Part 3

Here we are at the final part of our REST API tutorial, most of our project is completed. However, a true REST API should have more than one endpoint so lets add a few more since it’s been a while. After that i’d like to talk about some possible next steps that can be taken after this tutorial then we can wrap this up for good.

Adding More Endpoints

If you still have Stoplight open from the earlier tutorial then great, head back there. If not, just head back to stoplight.io, create another quick project, and paste the swagger.yml file from your local project just like last time. While we’ve stayed on the website for this tutorial, I actually recommend downloading Stoplight if you plan on using it in any capacity after this. That way, you will have an easier time managing your YAML files in future projects. 

We should be back at this screen here:

What we want to do is go back to the form section of Stoplight where we created our first endpoint. Let’s add a second one now, which will return a list of any questions we saved currently. I also want to take this opportunity to walk through how we can make custom objects to return to our endpoints.

We can do this by right-clicking on the models folder right beneath the Paths sections and selecting the New Model option. Name this model Question and hit Enter. This takes us to a new screen where we can add fields to a custom object:

Here is where we’ll add the fields from our Question object. In addition to the pre-populated ID, add three more strings for answer, category, and question.

After this, let’s create our next endpoint that will return a list of these Questions. Remember, we do this by right-clicking on the Paths folder and selecting a new path. From here, we type in our endpoint name which will be /questions in this case. Let’s fill out the description with something like, “return a list of questions.” 

Now when it comes to adding a response, we’ll use our Question object that we just created. Click on the ‘Object’ link within the Schema section and switch over to the $ref key. This will ask for a link to our Question reference to which we can paste:

 #/definitions/Question

Be sure to switch the response type from object to array as well. That’s about all we need for this endpoint before going back to our project, but let’s add one more endpoint before we do so.

Let’s pretend we might need to update a question in the future and we’ll need an endpoint for it. A PATCH call would be perfect for this. Ideally, if you have a POST call for creating some data, you’d use a PATCH call at that same URL for editing that data in the future. To do this in swagger, you would go back to your /question endpoint and where you see the POST tab highlighted, also select the PATCH tab and click the ‘+ PATCH Operation’ button.

For our request body, we’ll need the ID of the question we want to update as well as the updated information for the question. Let’s do this by adding the following fields to the default schema object:

  • id: integer
  • question: string
  • category: string
  • answer: string

For our response, we’ll return the same structure as the POST call for simplicity, a message string. Just like before, click on the code button above to get the updated yaml file that we’ll past back into our project. In your project, delete the old contents of your swagger file, paste in the updated version, and run make gen in the terminal. Swagger should have generated our two new endpoints, time to implement. 

Staying with our theme of simplicity, we’re going to re-use our questions slice from the PostQuestionHandler function. This will make it easier to implement the GET /questions endpoint. We wouldn’t normally do something like this in a production app, we’d usually have a database package or some other external service to handle accessing and manipulating the data. We’d also have more error handling. Let’s pull that questions slice out the PostQuestionHandler and update the Configure function to look like so:

 func Configure(api *operations.ATriviaApplicationAPI) {
   questions := []models.Question {
      {
         Question: "How many moons does Earth have?",
         Answer:   "1",
         Category: "Astronomy",
      },
   }

   api.PostQuestionHandler = operations.PostQuestionHandlerFunc(
      func(p operations.PostQuestionParams) middleware.Responder {
 

         q := models.Question{
            Question: p.Body.Question,
            Answer:   p.Body.Answer,
            Category: p.Body.Category,
         }

         questions = append(questions, q)

         return operations.NewPostQuestionOK().WithPayload(&operations.PostQuestionOKBody{
            Message: "Question added!",
         })
      },
   )
 }

When we add our next endpoint, we’ll return all the questions within this Question slice. Because we created another Question struct through swagger, we’ll have to convert our question structs from the models package to those types. We’ll have to create a nickname for this package since configure.go already imports our original models package. We can do that by pasting this at the top of configure.go, inside of the import parenthesis:

 genModels "github.com/Sutheres/gen/models"

Within the Configure function, beneath the first endpoint, our GET /questions endpoint code will look like this:

 api.GetQuestionsHandler = operations.GetQuestionsHandlerFunc(
   func(p operations.GetQuestionsParams) middleware.Responder {
      var payload []*genModels.Question

      for i, q := range questions {
         payload = append(payload, &genModels.Question{
            ID: int64(i),
            Question: q.Question,
            Answer:   q.Answer,
            Category: q.Category,
         })

      }

      return operations.NewGetQuestionsOK().WithPayload(payload)
   },
 )

Again, because we are not querying this information from a database table with IDs, we use the for loop to simulate our own within the genModels.Question object. The good thing about that is we can use those IDs to update a question in our PATCH endpoint. 

 api.PatchQuestionHandler = operations.PatchQuestionHandlerFunc(
   func(p operations.PatchQuestionParams) middleware.Responder {
    
      for i, q := range questions {
         if i == int(p.Body.ID) {
            q.Question = p.Body.Question
            q.Answer = p.Body.Answer
            q.Category = p.Body.Category
         }
      }
    
      return operations.NewPatchQuestionOK().WithPayload(&operations.PatchQuestionOKBody{
         Message: "success",
      })
   },
 )

And we’re done here. You can now run this and localhost:8080 and start interacting with this endpoint via curl or Postman. I hope you enjoyed this 4-part tutorial on how to set up a REST API via swagger in Go. Swagger allows you to do a lot more than we tried here, I suggest you take a look at it and give it a shot. Feel free to comment below with comments or suggestions.

Leave a Comment

Your email address will not be published. Required fields are marked *