Create a REST API Using Swagger in Go Part 3

This is the last of a three-part series about creating a REST API in Go. If you have not read the first two parts feel free to check those out here.

For the final part of this tutorial we’re gonna do some optimization and code clean up. Technically we have everything we need to build more endpoints, but this project would quickly become a headache if we kept moving in this direction. So let’s get started making this project more manageable.

Hello Makefile

One optimization that we could make is creating a Makefile. This isn’t specific to Go projects but it does make managing it a bit easier. Basically, we can create this file and plug in some terminal commands to be run at our leisure. A good first candidate would be when we want to update code in the project with the changes we’ve made to the swagger.yml file. 

If you remember in the first post we ran a command to generate some API code. The command wasn’t exactly easy to remember, but it’s a very important command to get right since it ends up getting used a lot over the course of a project. Let’s see how we can make things a little easier in regards to this, start by creating a file in your project called Makefile with no file extension. 

Paste the following into your Makefile:

.PHONY: setup gen clean

clean:
  rm -rf gen/*

setup:
  go install github.com/go-swagger/go-swagger/cmd/swagger
  go install github.com/golang/mock/mockgen

gen: clean
  swagger generate server -t gen -f ./swagger/swagger.yaml --exclude-main

We now have a functioning Makefile. The first line in this file is more for setup. Since Makefile commands typically refer to physical files in a system, this command will let it know that these commands don’t refer to physical files. Next is our first command, clean. This runs a terminal command to delete our gen folder in order to wipe our old generated code and use the updated version. 

Next is the setup command which is not needed for this tutorial but it is included anyway, it will basically download swagger and a mocking library for unit tests for us. I encourage you to research this on your own time if you feel up to it. Finally we have the gen command which will actually generate the new code for us. 

You may remember this command from part 1 when we generated code for the first time. It is a lot easier to remember that command as “gen” as opposed to the entire swagger command. The only change to this command is where we tell swagger to look for swagger.yml config file. Just for better readability, we’re going to put the swagger file inside of a folder in our project with the same name. So it would look like this:

/swagger
 --swagger.yml

 If you open a terminal within your project and run the command, “make gen”, you should see some logging that shows some code was generated. Since we didn’t make any changes to swagger.yaml it will be the same code that is generated, but at least we know it’ll work for when we make changes moving forward!

Question.go

Another small optimization we can make is to separate the Question struct to its own file called question.go. This will make our main.go file lighter in the long run, especially if we wanted to add more Question functionality. For a project of this current size it would make sense to create a models package for this. In Go project structures, it’s not uncommon to use what’s called an internal directory. This is where you put code that you don’t want exposed to other parts of the project. Create a folder named internal in your top level directory, create another folder inside that one called models, and place our new question.go there with the following:

type Question struct {
  Question string
  Answer   string
  Category string
}

Don’t forget to delete it from main.go once it’s copied over. 

Configure.go

Following along with the theme of removing things from our main.go file, the next optimization we can make is to relocate our API functionality. That place is configure.go which will also be in our top level directory. Inside that file, we’ll use a Configure function to manage our code like so:

func Configure(api *operations.ATriviaApplicationAPI) {


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

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

        fmt.Println(q.Question + “ added”)
        return operations.NewPostQuestionOK().WithPayload(&operations.PostQuestionOKBody{
           Message: "Question added!",
        })
     },
  )

You should have an error in main.go because the slice of Questions being unused now, go ahead and delete that along with the PostQuestionHandler code from main.go. Replace the PostQuestionHandler function with this line:

Configure(api)

If everything worked out right you should be able to run this project and hit the endpoint just like part 2, except we have a project with a much better structure now. 

final project structure

Wrap up

Now that we have a better structure, we can continue to build further by adding more endpoints and integrating other services in our project. While I won’t go too far into those topics on this series I did make a decision to wait until the next blog post to add the endpoints. This way, I can spend a few paragraphs to talk about what those other services could be and where to start looking into them. Stay tuned for the final part 4!

Leave a Comment

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