This is going to sound a little dismissive, but I don't mean it to be:
I'm not sure I understand the value that these frameworks offer beyond the HTTP server interface Golang supports out of the box, plus a URL router like "pat" (or whatever the cool kids are using now other than "pat").
I see the clean middleware abstraction, but I find the idiomatic closure-based implementation of middleware adds only a couple extra lines of code, and in return I get total flexibility.
What's this doing that I'm not seeing? I'm sure there's something; I'm writing this comment out of ignorance.
Actually, Goji grew out of a single deficiency in "pat": the fact that it does not have a standard way of defining request context.
The big use case here is how you'd write a middleware that did authentication (using API keys, session cookies, ???) and emitted a username for other middleware to consume. With net/http, you end up with a lot of coupling: your end handler needs to know about every layer of middleware above it, and you start losing a lot of the benefit of having middleware in the first place. With an explicit middleware stack and a universal interface for middleware contexts, this is easy: everyone can code to the same single context object, and instead of standardizing on weird bound variables (or a global locked map a la gorilla), you just need to standardize on a single string key and a type.
I think my ideal world would involve Go providing a map[string]interface{} as part of the http.Request struct in order to implement this behavior, but until we get that, I think Goji's web.C ("the context object") is the next best thing.
There's one other thing pat hacks around: the issue of how to pass bound URL variables to the resulting handler. At first I was a little grossed out at how pat did it, but I've sort of come to terms with it. I still think Goji's way is better, but I don't think it's the reason I wrote (or a reason to use) Goji.
I agree that 'context' and passing this context between middleware is the biggest missing item from the standard http+"pat".
This library (Goji) solves it with a map[string]interface{}. This works, but has the downside that you need to type-assert your values from this map if you want to use them.
I wrote a library a while ago (http://www.github.com/gocraft/web) that uses reflection so that your contexts can be strongly typed. Martini offers a similar approach.
Unless I'm not understanding this correctly, this sounds exactly like how middleware state is implemented in Ruby's Rack and it is one of the biggest warts of Rack that the core team is trying to fix.
Yeah, I looked at gocraft/web for a long time before writing Goji. It's a good library, and I think it does a lot of things right. But at the end of the day, just like my disagreement with Martini, this comes down to a difference in principles. I think there are two theories of Go library design here. One of us has chosen one, one the other, and let me prefix this by saying that I'm not sure the way I chose is correct.
On one hand, a library can allow its users access to typed structs—this is the way gocraft/web does it. The upside here is huge: applications are now type-safe, and can lean on the compiler to enforce their own correctness. The library code that backs this behavior can be arbitrarily gnarly, but you only have to write it once, after which it'll Probably Be Correct (tm).
On the other hand, you can provide a standard "universal" context type like Goji does. Since the library makes no statements about the sorts of fields you have or their types, applications have to do a bunch of shenanigans with type casts in order to extract the typed values they expect. The upside here, however, is the standardization and what that allows. I've found that in most of the web applications I've built, only a small fraction of the middleware is authored by me: I end up pulling in the standard request logging middleware, or the middleware that assigns request IDs, or the middleware that does CSRF protection. There's a huge amount of value in being able to write (read: "let someone else write") those middlewares once and being able to use them in any application. With arbitrary application-provided structs, this isn't possible [1], but if you can instead standardize on types and keys out-of-band, you can mix and match arbitrary middleware with impunity.
This alone is one of those awkward engineering tradeoffs where you're exchanging one nice thing (application-level type safety) for another (standard interface, and hopefully an ecosystem of drop-in middleware), and given just this I'd argue that the ecosystem is probably more important. But the frosting on this particular cake is that, with only marginally more awkwardness, you can actually get application-level type safety too: look at what I did for the request ID middleware that ships with Goji, for instance (https://github.com/zenazn/goji/blob/master/web/middleware/re...). Again, it's not quite as nice as a user-defined struct, but in the grand scheme of tradeoffs and sacrifices, I don't think it's so terribly bad.
So TL;DR, Goji chose standardization and composability in the design of its context object over application type safety, but if you're willing to write a few helper functions I don't think you end up losing the type safety anyways.
[1]: actually, come to think of it, with a lot of reflection and some struct tags you could probably pull it off. (Something like "struct { RequestID string `context:"reqid"`}", and the request ID assigning middleware would hunt around for a string field tagged "reqid"). But it seems like it'd introduce a lot of coupling between middleware and the application which I'm not sure will turn out well—any time a middleware needed a bit of extra storage, every application would have to change. In any event, if someone builds this I'd love to play around with it.
Generally, what I don't like about web frameworks in Go is that they often don't integrate too well with the existing net/http package. I think we don't need web frameworks, we need web libraries that we can easily plug together as we want without any additional glue.
I started with Gorilla due to its helpers with cookies. Then Martini came along and I had the same opinion as you, but soon realized that there are some nice utilities that help common scenarios.
I always think to myself that those nice utilities that I would have to build each time myself will some day bite me in the butt when they have abstracted too far from the standard http request and response.
I sometimes have to write in C# MVC, which is a poor copy of the Rails framework. It's such a nightmare to do things in a way the framework did not intend. I now remind myself that there are no free gifts when it comes to web frameworks.
This is more or less the philosophy of the Clojure community when it comes to web frameworks. Not sure what you're reasons for using Go are, but if you're open to dynamic typing you would probably appreciate Clojure's emphasis on avoiding this kind of complexity.
Though to be honest I do see a decent amount of value in not rewritng form handling/validations/auth in a bunch of different ways.
I like Clojure a lot, and if I was building a new web app today, I'd strongly consider using it instead of Rails (for what it's worth: Golang is great for JSON web services, but not IMO great for full-features web apps, even if you're using something like Angular and a "single-page" type architecture; you pay a huge price in flexibility for it).
However, to talk myself out of using Rails, I'd need to convince myself that I would (a) be able to use Postgres and (b) would not be writing lots of SQL. I can write SQL, and have been writing it for a long time, and I know that writing and maintaining it slows me down.
"even if you're using something like Angular and a "single-page" type architecture; you pay a huge price in flexibility for it" Can you elaborate more on that statement? I use angular/golang api and rails/angular both often.
When I wrote a single-page Angular app with a Go backend, we barely did any of the HTML generation on the Go side, basically just enough for AngularJS to take over, which was essentially just one index.html template. I found it worked really well as an AngularJS backend.
I previously did a multi-page using html/template and I found it a bit more painful, especially since at the time there wasn't a ready-to-use solution for dynamic template compilation during development and then static compilation in production. I'm not sure if there is now, but I haven't looked in quite some time.
As for the template html/template language itself I've kind of grown to like it, but maybe that's just me ;)
One solution might be to include a javascript runtime in your app such as monkey (https://github.com/idada/monkey) , load a javascript template framework like underscore.js(http://underscorejs.org/) into the runtime context and create a render function that takes a JSON string and template string as parameters. You can then write the return value back into your "http response".
It's a bit of a hack/convoluted solution but it allows you to make use of a few javascript template libraries which are IMHO more pleasant to work with.
Ahh thanks. Yeah, almost all of the apps I have written lately are a rest/json api service in rails or golang (martini) and the client side code is angular, ios, etc. So I don't care nor use html templating in go.
What did you use for your db backend with Go? Gorp, beedb, database/sql, other? Just curious, as I've been looking at building some api's with go and martini lately.
The short answer is no. There's honeysql which basically lets you express sql in terms of Clojure's datastructures so you can get more reuse than you can with straight SQL strings, but it's not really an ORM as I understand it.
It seems to me that Rails' main advantage over Clojure+microframeworks for web development is the ability to use mature libraries to handle common tasks: user management with devise, for example.
I'm not sure I understand the value that these frameworks offer beyond the HTTP server interface Golang supports out of the box, plus a URL router like "pat" (or whatever the cool kids are using now other than "pat").
I see the clean middleware abstraction, but I find the idiomatic closure-based implementation of middleware adds only a couple extra lines of code, and in return I get total flexibility.
What's this doing that I'm not seeing? I'm sure there's something; I'm writing this comment out of ignorance.