My boilerplate for building Go web services
I had to deal with building web services in multiple stacks in the past couple of years. From PHP, NodeJS, Python to Ruby. It's been interesting. When I started building web services with Go for production or side projects, the general practice I brought in from my past experience was to pick a framework. This made sure that some of the problems are already solved and there was small learning curve but not very steep. One thing I failed in the start was to understand that this is not the case with Golang, purely because, for really simple services, you can get away without a framework. And after going through multiple iterations writing different services, I managed to finalize a stack that worked best for me. Personally, this works fine for my side projects. Purely because I opted for clarity over premature abstractions. Having a few extra lines of code is fine because it helps the next person who tries to work out the not so magical code base.
Right now, these are the tools I use to build web services.
- Routing - Gorilla Mux gorillatoolkit.org/pkg/mux
- Tokenization - JWT github.com/dgrijalva/jwt-go
- Database migrations - github.com/pressly/goose
- ORM - gorm.io
- Validations - github.com/go-playground/validator
- Env variables management - github.com/joho/godotenv
To justify, the reason to have Goose and GORM both when I can do both in GORM, is because I wanted to have my migrations in plain SQL. Most database schema abstraction layers have limitations and workarounds for some really simple stuff like getting ENUM to work, or defining and managing relationships and rules. Also, they have zero support to manage any SQL stored procedures/functions. This is from experience, as I found it easy to manage databases outside any framework. As long as I can have steps for running the migrations incrementally. And they can be versioned.
Another very common problem we face when writing up a new service is figuring out where to put what. And how to structure the code in modules. This is a valid question and surely saves a lot of time when the project gets large. But, the majority of smaller projects remain small or never exceed a few files or handlers. Due to this, I opted to just have a simple file structure when I start with the projects.
In this way, for smaller projects, the code structure remains the same. And everything is just in the main package. When I want to start extracting stuff into modules, I start by refactoring them into their own files firs but leave them in the main package. For instance, handlers.go would become ie. program_handler.go, and middlewares.go would get extracted into jwt_middleware.go
This allows incremental refactoring as an when required. And only when required.
Testing follows the same pattern and test files would sit next to the main files. Allowing easy refactoring when the time is right.
There are a bunch of tools that will allow you to manage package dependencies. In my case, I moved over to Go Modules from Dep. There is good article about this here blog.golang.org/migrating-to-go-modules
Apart from these, I use the defacto tools Docker, docker-compose, Make for various other tasks. I am not using a hot reload package for Go. Never got frustrated having to rebuild because the build times are pretty fast. Maybe this might change if I have something much larger.
If you have a different approach to how you build stuff with Go. Please do comment below. This is just a highly opinionated way of how I write web services with Go...