Live data from Hacker News

Goji: a web microframework for Go

goji.io

71–80 of 88 posts

Re: Goji: a web microframework for Go

#72
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

> 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)

Re: Goji: a web microframework for Go

#74
post #73

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

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/

Re: Goji: a web microframework for Go

#75
post #58
post #17

Earlier quoted context omitted.

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 ab…

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 str…

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.

Re: Goji: a web microframework for Go

#76

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…

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) which I think covers this.

Re: Goji: a web microframework for Go

#77

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, G…

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.

Re: Goji: a web microframework for Go

#78
post #60

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.

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.

Re: Goji: a web microframework for Go

#79
post #78
post #60

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.

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 y…

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".

Re: Goji: a web microframework for Go

#80
post #54
post #52

Earlier quoted context omitted.

"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.

I find the HTML templating options for Golang particularly painful, is the subtext there. (I like Golang a lot; we did the bulk of microcorruption.com in it. But the web front end, which is a tiny amount of code, that's a Rails app.)

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.

Post reply on HN