When Martini was released, it quickly became the most popular Go web framework and it still is. But it is not idiomatic, it is slow and the concept is flawed. It taught bad practices to a lot of new Go developers. Despite these facts, lots of developers are using it because it is very easy to get started with it and it's a very good reason!
Since then, most resources on writing web applications I've read are only about basic things, so I decided to write a series of articles on building your own web framework by using existing libraries or writing components from scratch. It will teach best practices to new Go web developers and remind older Go developers what best practices are for web development. This article serves as an introduction and a table of contents.
Remember that each project is unique and the best practices presented here may not be the most practical to a particular project. As long as your code is working and is maintainable, it's good enough. But when sharing code, it's best if everyone has more or less the same practices.
At the end of the series, you will understand not only the best practices of web development in Go but you will also know about different solutions for each problem a framework solves and be able to apply the right solution for each project.
- Part 1: Introduction
- Part 2: Middlewares in Go: Best practices and examples
- Part 3: Share values between middlewares
- Part 4: Guide to 3rd Party Routers
- Part 5: How to implement JSON-API standard with MongoDB
- Bonus: File Upload REST API with Go and Amazon S3
What really are the features of a web framework
There are two main types of web frameworks. Rails-like frameworks with everything built-in so you can bootstrap your project really quickly. In Go there are some frameworks like this: Beego and Revel.
And there are Sinatra-like frameworks with a router and a few built-in features but you must bring your own ORM and lots of features have to be added. Most Go frameworks have adopted this style: Martini, Goji, gocraft/web, etc.
Frameworks vs Libraries
There has been a recent debate on using frameworks vs using libraries. I'm not against frameworks but I think Go has great small packages that we can make a framework of our own pretty easily. If you use Go on long-term projects, I think it is a wiser choice to do it yourself. If you want to learn Go, it is also better to understand how everything works. And that's what I will be talking about in the next part of the series. If you're not aware of this debate, look at these following articles:
- The Case for Go Web Frameworks
- Go, frameworks, and Ludditry
- Frameworks: Necessary for large-scale software
- Framework vs Library
Which features to include in our framework
We will focus on the latter type because covering every feature in Beego or Revel would mean writing an entire book on the subject!
A framework has 3 parts. A router receiving a request and directing it to a handler, a middleware system to add reusable pieces of software before and after the handler, and the handler processing the request and writing the response.
- body parsing
The current state of Go web frameworks
The problem with many Go web frameworks is that these components are made for one and only one framework. They are not inter-changeable. Yes you have just routers, or just middleware systems, or just middlewares and you might use them together if they're compatible with each other. But it's a very small minority of projects and it doesn't have enough visibility except for Gorilla which has still far less followers on Github than most other frameworks.
Go doesn't have a unified convention to handle middlewares like in Ruby (Rack), Node.js (Connect.js), Clojure (Ring) and other languages. Negroni, Goji, Gin, gocraft/web, etc, are all redefining how handlers and middlewares work. It's hard to re-use open-sourced middlewares, and it's hard to understand what are the best practices for a new developer.