
Goji: a web microframework for Go - zenazn
https://goji.io/
======
tptacek
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.

~~~
zenazn
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.

~~~
cypriss
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](http://www.github.com/gocraft/web)) that
uses reflection so that your contexts can be strongly typed. Martini offers a
similar approach.

~~~
zenazn
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...](https://github.com/zenazn/goji/blob/master/web/middleware/request_id.go#L55-L69)).
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.

------
aalpbalkan
It is good to have many web microframeworks in a language ecosystem. In Python
probably there are a hundred of those. Many of those are not picked up by the
community –a natural selection. Only really a few of those survived. It all
depends on you if you are going to choose Revel, Martini, Goji or whatever you
want. Today, thousands of apps run on web.py, yet most of the source code is
untouched last 3-5 years
([https://github.com/webpy/webpy/tree/master/web](https://github.com/webpy/webpy/tree/master/web))
It's impressive it just works!

Personally, I am looking for frameworks that many people rely on, maintained
frequently as needed and works just fine. There could be a +-10% difference on
QPS those framework URL routers can handle and render a 'hello world' page.

So this is a nice attempt I would say, looks cleaner than Martini, still
supports middlewares. On the other hand, Martini has support to serve static
files, logging, panic recovery, which are also good and has a bigger fanboy
community around it: [https://github.com/go-
martini/martini](https://github.com/go-martini/martini)

~~~
zenazn
Static files:
[http://golang.org/pkg/net/http/#FileServer](http://golang.org/pkg/net/http/#FileServer)

Logging: [http://golang.org/pkg/log/](http://golang.org/pkg/log/)

Panic recovery:
[https://godoc.org/github.com/zenazn/goji/web/middleware#Reco...](https://godoc.org/github.com/zenazn/goji/web/middleware#Recoverer)

Not as many fanboys though (yet!)

------
kohanz
Perhaps this is not the best place for this question, but as a frequent HN
reader, I'm constantly told that Go is great to develop in and very
performant. However, it's not clear to me how Go suits a web application with
relational data.

From what I've gleaned, an ORM does not make sense in Go, so how would this
type of application be approached? Writing a lot of ORM-type boiler-plate? A
completely different way? Or is Golang a bad choice for such an application?

~~~
sagichmal
I suppose the Go philosophy would discourage developers from tightly coupling
Go types and their relational representations through an ORM. The idiomatic
way of mapping your objects to a database is by defining a thin interface
around a sql.DB, with first-class operations for your concrete types.

    
    
        type User struct {
            ID        int
            Permalink string
        }
        
        type Storage sql.DB
    

Then you can either do

    
    
        func (s *Storage) WriteUser(user User) error {
            if _, err := s.Exec(
                "REPLACE INTO users VALUES (?, ?)",
                user.ID,
                user.Permalink,
            ); err != nil {
                return fmt.Errorf("write user failed: %s", err)
            }
            return nil
        }
    

or

    
    
        func (u User) Write(storage Storage) error {
            if _, err := storage.Exec(
                "REPLACE INTO users VALUES (?, ?)",
                u.ID,
                u.Permalink,
            ); err != nil {
                return fmt.Errorf("write user failed: %s", err)
            }
            return nil
        }
    

It's a bit more laborious in the sense of keystrokes, but it's also more
explicit, which is, on balance and over the lifetime of a large software
project, a good thing.

~~~
kohanz
Thank you for the detailed answer.

 _It 's a bit more laborious in the sense of keystrokes, but it's also more
explicit, which is, on balance and over the lifetime of a large software
project, a good thing._

OTOH, it feels like a high development cost to pay, relative to the
alternatives out there. You certainly wouldn't use it for an MVP. Also, it
would increase the opportunities to introduce bugs into the application
(unless one actually feels they can write something better than, say
ActiveRecord, from scratch).

I have a side-project in RoR that would benefit greatly from the performance
boost provided by Go. However, the idea of writing all of the ORM
functionality that ActiveRecord handles for me (not just managing of objects
as you've shown, but the relationships between them) is quite daunting.

~~~
sagichmal

        > (unless one actually feels they can write something 
        > better than, say ActiveRecord, from scratch).
    

The point is that ActiveRecord's method of modeling, especially when it comes
to dynamically mapping language constructs to a storage layer via SQL, is too
implicit. Too costly. Actively harmful! The point is to get developers to stop
thinking in terms of ORM abstractions, and start thinking in terms of the
actual transforms and manipulations that are occurring.

    
    
        > You certainly wouldn't use it for an MVP.
    

I think you overestimate the cost of pressing buttons on your keyboard.

~~~
kohanz
_The point is to get developers to stop thinking in terms of ORM abstractions,
and start thinking in terms of the actual transforms and manipulations that
are occurring._

Isn't the point also to encourage code re-use and abstraction? I mean, golang
has packages for a reason. I suppose my question is more along the lines of
whether we'll ever see a package in go that would standardize the data-object
divide or whether this will always be a "roll your own" domain?

 _I think you overestimate the cost of pressing buttons on your keyboard._

Then why don't we "roll your own" for everything?

~~~
sagichmal

        > I suppose my question is more along the lines of whether 
        > we'll ever see a package in go that would standardize 
        > the data-object divide
    

There is no lucid standardization possible for the data-object divide, in Go
or any other language. Too much depends on the semantics of the object and
data system. Or, rather said: that standardization (that abstraction) is SQL
itself.

------
codegangsta
Looks very nice. I do appreciate having more clean, well thought out web
frameworks in the Go space. Type switches for your handlers is a good way to
approach the net/http compatibility.

There are some people that find that Martini is a bit too magical for them,
and that is completely okay. It's great to see another minimal framework that
will suit their needs.

------
Matrixik
About this part in README:

> I have very little interest in boosting Goji's router's benchmark scores.
> There is an obvious solution here--radix trees--and maybe if I get bored
> I'll implement one for Goji, but I think the API guarantees and conceptual
> simplicity Goji provides are more important (all routes are attempted, one
> after another, until a matching route is found). Even if I choose to
> optimize Goji's router, Goji's routing semantics will not change.

Maybe you can just use HttpRouter without reimplementing it yourself?

[https://github.com/julienschmidt/httprouter](https://github.com/julienschmidt/httprouter)

> The router is optimized for best performance and a small memory footprint.
> It scales well even with very long pathes and a large number of routes. A
> compressing dynamic trie (radix tree) structure is used for efficient
> matching.
    
    
        goji.Get("/hello/:name", hello)
    
        router := httprouter.New()
        router.GET("/hello/:name", Hello)

~~~
zenazn
Huh. I'm not sure I saw that particular project during my search. It looks
neat!

Without having looked in detail at httprouter, I think the most obvious reason
it might not be sufficient is that it doesn't support regular expressions.
This might not be a dealbreaker, but I'm fond of the occasional regex route,
and I'd have to think long and hard about whether it's worth giving up for a
faster router. And plus, I'm still not sure router speed actually matters for
most applications.

In any event, I _do_ have a long plane trip coming up, and I'm sure Goji will
grow itself something at least slightly more efficient than a linear scan
then. I'm think Goji's router and middleware stack are already zero-
allocation, so it'll just be finding a way to binary search through routes.

~~~
Matrixik
You can find benchmarks in pull request for
[https://github.com/cypriss/golang-mux-
benchmark](https://github.com/cypriss/golang-mux-benchmark):

[https://github.com/cypriss/golang-mux-
benchmark/pull/5](https://github.com/cypriss/golang-mux-benchmark/pull/5) (the
last one)

------
DennisP
If you think you're ready, it might be fun to submit a techempower benchmark.

[http://www.techempower.com/benchmarks/](http://www.techempower.com/benchmarks/)

------
nemothekid
I see why some would call Martini "magic" but I'm not entirely sure I prefer
having to deal with a giant `map[string]inteface{}`. What you are really doing
is moving the "magic" from the framework and onto the developer (I now have to
do type checking and casting).

That said I'm a huge fan of Martini and I actually use codegangsta's Inject in
my other projects to manage shared state/resources, so I am heavily partial to
it.

~~~
zenazn
Yeah, this is a good question, and the unfortunate answer is that it was an
engineering tradeoff. I wrote a pretty long reply to cypriss
([https://news.ycombinator.com/item?id=7632956](https://news.ycombinator.com/item?id=7632956))
which I think covers this.

------
zkirill
Great job! It definitely feels like a microframework compared to others. I'm
glad that there are so many starting points for Go web services available now
of varying levels complexity. To me this feels like it fills the void between
Gorilla and Revel/Martini/Beego. Also, the code is very well documented and
easy to follow.

------
mcescalante
Every time I see a web framework for Go, I just want to see an example or two
of a website developed with it. Does anybody have any solid examples?

Hopefully, like me, some others enjoy exploring existing code as well as
reading the examples / docs.

~~~
codegangsta
I can't speak for Goji. But one popular open source Martini app is Gogs
[https://github.com/gogits/gogs](https://github.com/gogits/gogs)

------
gkya
Call it nitpicking, but I would rather not export a symbol 'C' from a library
I write. Seriously, is 'Context' that hard to type? And the author seems to be
far from lazy, the codebase is nicely and extensively commented (it is a nice
read indeed). Apart from this issue, the library seems quite nice.

------
ya3r
Do we yet have the Django for Go? Goji is as said a microframework, what I
want is an equivalent of Django.

~~~
hackerboos
I doubt this will be produced.

Microframeworks written in Go are more likely to replace Django Rest Framework
[1] than Django itself

[1] [http://www.django-rest-framework.org/](http://www.django-rest-
framework.org/)

------
ddoolin
A bit off-topic, but I love the site colors. I'd love that text theme for Atom
if it's available?

~~~
GuiA
Orange (keywords): #fd971f

Red (strings): #f24840

Green (variables): #96c22e

White (text): #ffffff

Grey (background): #222222

------
abbot2
When I read things like "func Get(pattern interface{}, handler interface{})" I
start questioning why this whole thing is ever written in Go at all? This kind
of ditches half of Go's benefits by moving all type checks to run time.

~~~
zenazn
If Go supported method overloading, you could actually write the types of
those functions out. The first one is either a string or a regexp.Regexp, and
the second one is one of four variations on an http.Handler, giving a total of
8 varieties of each function.

I decided that the sin of exposing an interface{} as a parameter was less
egregious than the sin of multiplying Goji's API surface area by a factor of
8, but you'll be happy to know that passing a value of the wrong type causes
the invocation of Get (Post, etc.) to fatally exit immediately. If you're
defining all your routes in a single goroutine before calling goji.Serve()
(which is probably the most common way to define routes), your application
will crash before it even binds to the socket.

So, not quite as good as a guarantee enforced at compile time, but it'll have
to do.

~~~
abbot2
Don't get me wrong, I perfectly understand what interface{} is and why someone
is tempted to use it. It just kills the type checking and converts your golang
code to a compiled python, without all those nice static analysis things. Yes,
API would be larger, but it would be compile time type-checked and you
wouldn't depend on things like "well, it will most probably crash very early
enough".

------
levosmetalo
Just a quick look at the examples, and I can say that it reminds me very much
of Clojure Ring. It provides really small and extensible core, and if the
community pick it up and start writing useful middlewares, it can become very
useful.

------
gfalcao
Were you inspired by this ?
[http://www.cherrypy.org/](http://www.cherrypy.org/) The juxtaposition of
things and colors and code simplicity looks like so

------
tete
How does this compare with Martini?

[http://martini.codegangsta.io/](http://martini.codegangsta.io/)

------
jodiscr
I ain't switching from Perl 5.8 to golang until a shared hosting provider
becomes available.

~~~
julien_c
Google App Engine

------
leccine
Sorry for my ignorance, how is this different, better than Martini? What is
the main goal of creating a new framework (instead of getting the features you
are missing implemented in the currently existing ones)?

~~~
zenazn
To be perfectly honest, I'm not sure it is better (I was hoping you would help
me decide that!), and I wrote it mostly because every aspiring programmer
writes a web framework at some point, and it was time I wrote mine (it was a
lot of fun :) ).

But I think there's a good chance it is better.

First, I think one important difference is that Goji isn't full of magical
reflection. If Go had support for method overloading, its entire interface is
type-safe. In contrast, Martini does a lot of magical object injection, and
it's not clear until runtime if your routes will even work, or what they'll
even do, or where exactly the memory for them is coming from.

Second, I much prefer Goji's way of defining middleware. To me, middleware is
like an onion (just like ogres!): each layer is a wrapper around the previous
one. The way you write middleware in net/http is by wrapping the old
http.Handler with a new one, and that's how I wanted Goji's middleware to work
too. There's no magic "context.Next()", there's no magic dependency injection
overrides, it's just http.Handlers all the way down.

Anyways, I'd like to know if you think I'm right: again, I'm really not sure
this is actually better than Martini (or $YOUR_FAVORITE_FRAMEWORK), but I
think it comes from a slightly different set of principles, and ones that I
think are worth considering.

~~~
beefsack
The fact that you're trying to stick with strong typing as much as possible is
very appealing to me, and now I'm convinced to try to port my new project from
Martini to Goji.

I think this is a good point to differentiate yourself on, and perhaps it
should be included in your elevator pitch.

