Full stack monolith experiment with Golang

Full stack monolith experiment with Golang

I recently built a project to help me deploy servers fast, and prevent me from doing the same mundane stuff over and over again.

It's not the sexiest project around and a quick MVP - so the code is not as clean as I would like. Eventually, I will re-factor and neaten things up. However, as an experiment with Golang - it was a good learning experience.

I have never used Golang for a web app, my general use case for Go are small to medium background jobs, terminal tools, and scraping.

Why Golang for a webapp?

Why not? , there's something about minimalism that really resonates with me. I like to keep things clean and lean. I don't need hand-holding and batteries included all the time.

Golang, just out of the box is so compact, however, the standard library provides so much value that you don't really need to reach for 3rd party libraries often.

The tooling I used previously pulled in so many packages and set up a ton of folders. This is great to get up and running quickly, but do I really need all that bloat upfront? why can't I just pick and choose packages as I need them?

Golang is productive

The language is compact and easy to learn. It doesn't have weird verbose constructs like Java or other OOP languages, yet it's fast in execution and development time, furthermore, you get type-safety out of the box without having to do too much.

I initially used the net/http router, the templating library, and the MySQL adapter available in the Golang standard library.

Just with these standard packages - I could build out routes, set up middleware - make DB queries, and even render templates.

No Django, Rails, Next, Laravel - just Golang, I could build an MVC app. You might think this would be a slow process but actually, it's not.

By the time you pip install, composer install or even npm install. I've already set up a route, and a controller method and pulled in data from MySQL.

Laravel Eloquent was amazing to work with, however, I kept forgetting the fields - sure you can explicitly define these and the IDE will pick them up. However, in Golang - structs are lightweight and easy to set up.

The IntelliSense throughout the codebase is amazing. Having that fluidity, as I type "User." just keeps me writing code like poetry, with speed and a touch of flair.

Pulling in just the packages I need

While the standard library took me quite far, there came a time when I needed a little bit more.

The MySQL adapter was okay but a nice ORM would be great, this is when I reached for GORM - it's such a beautiful and easy package to work with.

You can combine structs with GORM, similar to Laravel's Eloquent. Further, you can also run raw SQL - so kinda like the best of both worlds.

Here's an example:

var book Book
db.First(&book)

# Where
db.Where("isbn = ?", "xyz").First(&book)

# Raw Query
var totalSales int64
db.Raw(`
SELECT SUM(price) FROM orders WHERE category_id = ?`,
5).Scan(&totalSales)

Next, I added more and more routes, it was becoming a bit cumbersome - so I reached for GIN. GIN is a very lightweight web framework for Golang. In GIN you can declare routes similar to the below:

router.POST("/server/firewall/delete/rule", controller.DeleteFirewallRule)
router.POST("/server/firewall/add/rule", controller.AddFirewallRule)
router.GET("/sshkeys", controller.SshKeys)

Apart from these 2, and db drivers - there are not too many other packages that I have used.

What features does my app have?

It took me roughly 3 months, 1 to 3 days a week to work on this project whenever I had a bit of free time, which when I look back really shows how productive Golang can be. Here are the features I built:

  • Full authentication: login, logout, password reset, detect and ban too many failed attempts.

  • Teams functionality: invite and manage other teammates.

  • Email functionality: sends out registration emails, forgot password, and so on.

  • SSH jobs: jobs that will provision servers including setting up SSH keys, MySQL, Laravel, PHP, and so forth.

  • CRON job management: Set up and sync CRONs from the GUI.

  • Firewall management: add and delete firewall rules from the GUI.

  • Extended GIN templates to use a jinja2 package.

  • 2Factor auth - I used a 3rd party library but still needed to implement the flow.

... And the list goes on. You can view the full project here:

https://github.com/plexcorp-pty-ltd/scriptables

Conclusion

Golang is quite capable of building a full monolith backend, with just a handful of hand-picked packages - you can reach the same level of productivity you would achieve in any of the major frameworks.

Go's not perfect though, there are some areas it falls short in and you have to implement your own solution:

  1. Laravel and Django templates are awesome to work with. There's nothing as feature-complete as these two in Golang. Nowadays, most are using a frontend framework like React or Vue - so this is probably not a major issue.

  2. There are no established guidelines. In Django and Laravel, every project has the same sort of structure. Golang on the other hand can vary depending on the team, developer, or company.

  3. Some plumbing stuff would be great. Implementing CSRF in GIN was a bit annoying, this should just come standard. Auth scaffolding, forgot password - that kind of thing would be nice too.

Overall I had fun experimenting with Golang, not sure if I will use it again for bigger projects but for small to medium web apps - why not?