usually for stores like this I just do this: type Store interface { GetTask(*Task) error } instead of having "GetTaskByID" and "GetTaskByTag" and whatnot. Then in the caller you just do this: task := store.Task{ID: 5} if err := db.GetTask(&task); err != nil { // wahtever } ^ that gets the task by ID task := store.Task{Tag: "foo"} if err := db.GetTask(&task); err != nil { // wahtever } ^ that gets the task by tag.
REST Servers in Go: Part 1 – standard library
111–120 of 149 posts
Re: REST Servers in Go: Part 1 – standard library
#112Good introduction. A few thoughts: 1) Be careful with locks in the form "x.Lock(); x.DoSomething(); x.Unlock()". If DoSomething panics, you will still be holding the lock, and that's pretty much the end of your program. ("x.Lock(); defer x.Unlock(); x.DoSomething()" avoids this problem, but obviously in the non-panic case, the lock is released at a different time than in this implementation. Additional tweaking is re…
type HTTPError interface {
GetHTTPCode() int
}
func ServeHTTP(w http.ResponseWriter, req \*http.Request) {
result, err := DoTheActualThing()
if err != nil {
statusCode := http.StatusInternalServerError
if httpError, ok := err.(HTTPError); ok {
statusCode = httpError.GetHTTPCode()
}
http.Error(w, ..., statusCode)
return
}
w.Header().Set("content-type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(result)
}
You can apply the same approach for the HTTP body in case of error.Re: REST Servers in Go: Part 1 – standard library
#113I'm an API developer working with Python and Django. I did dabble with Golang for quite some time, but I just can't seem to justify the effort (in terms of lines of codes and static typing) of writing a ReST API with Go when I can build a similar one with Django and co (DRF, Swagger, etc). Can someone chime in? There must be an obvious advantage that I might be missing.
running python servers is annoying, you have to have a menagerie of stupid little parts and things to get it all to fit together. I haven't done this in years but I always wound up with some mess of virtual envs, pip, gunicorn, nginx proxying, something to start the services, and that's _before_ writing any of my own code. With Go I just compile a static binary, rsync it to a server and turn it on and call it a day.…
With "turning it on", you mean you write a systemd configuration file and start it using systemctl, right? Just curious how people do this stuff nowadays.
Re: REST Servers in Go: Part 1 – standard library
#114I would probably go with gRPC + grpc-gateway[1] instead. Declaring your services and models in proto files, annotating your services with google.api.http to help grpc-gateway scaffold your HTTP base. Then just implement your services from the interface generated by grpc-go. You can even register your gRPC services to grpc-gateway without actually bringing up a gRPC server. You finally end up having your exact data mo…
You only get a certain number of complexity tokens and IMO it's not worth spending any here.
Re: REST Servers in Go: Part 1 – standard library
#115Earlier quoted context omitted.
What you're pointing to is the need for better abstractions and Go is not the language for that (it will be more-so when generics arrive). There is a language that has faster-than-go speed and better abstractions, but HN seems to be somewhat decided on whether they think it's awesome or terrible, and there was recently an blog post on front-page about how it was bad for APIs (which I heavily disagree with but I am bi…
Are you talking about Haskell? That’s a hard sell.
If we're talking just pure abstraction I think there are a lot of other choice that could have delivered similar improvements in performance for an IO-dependent workload, with better methods of abstraction, ecosystem, and safety -- that choice for me would be Typescript.
[EDIT] Also to note, even as a Haskell zealot I'm not crazy enough to suggest someone choose Haskell as an alternative where Go would have been good enough. I have enough experience with business needs to know that the purity/safety/whatever other benefits of haskell just aren't worth the lack of ecosystem, difficulty in finding developers, and hit to developer speed. Haskell is too far on the spectrum (on various axes) to be the right choice most of the time, and not enough companies have shared how they've outperformed with it to even start the conversation. Haskell is like the mercedes of programming languages -- airbags show up there first, but regular cars get them eventually.
Re: REST Servers in Go: Part 1 – standard library
#116Good introduction. A few thoughts: 1) Be careful with locks in the form "x.Lock(); x.DoSomething(); x.Unlock()". If DoSomething panics, you will still be holding the lock, and that's pretty much the end of your program. ("x.Lock(); defer x.Unlock(); x.DoSomething()" avoids this problem, but obviously in the non-panic case, the lock is released at a different time than in this implementation. Additional tweaking is re…
Just want to address (1) quickly. As you've mentioned at the end of the parenthesized note, the reason I did not use `defer` here is to avoid the lock staying across the rest of the handler. I wanted to confine it to the datastore interaction.
Thinking more about this now and having read the comments, I'm considering to just hide the lock in TaskStore and avoid all these explicit locks/unlocks in handlers; it seems like it will avoid some confusion for folks reading the example (as well as quite a few lines of code!), and my goal here is really the HTTP server logic. I prefer to deflect any attention from TaskStore in this series of posts.
Re: REST Servers in Go: Part 1 – standard library
#117I would probably go with gRPC + grpc-gateway[1] instead. Declaring your services and models in proto files, annotating your services with google.api.http to help grpc-gateway scaffold your HTTP base. Then just implement your services from the interface generated by grpc-go. You can even register your gRPC services to grpc-gateway without actually bringing up a gRPC server. You finally end up having your exact data mo…
gRPC feels to me like a violation/break in expectations of how the layers of abstraction are supposed to work -- gRPC works over HTTP/2, and it's weird that it translates a lower level (HTTP/1) at a higher level (gRPC) to re-encode it into something at the lower level. An appropriately robust protocol would simply support both lower levels of abstraction if it wanted to (and they were widespread in daily use), right?
The only places I feel like I see this kind of layer violations are in lower level networking and it generally just makes everything worse and more complicated there. Of course, I know that gRPC + gateway is actually not a huge deal in practice -- you've got reverse proxies like envoy that will do it for you automatically[0], but it just... doesn't sit great. The benefits of gRPC are not to be sneezed at (better performance, strict typing at the protocol level, schema enforcement, bidirectional streaming, etc), but it feels like it could have accomplished a lot of those goals without throwing out HTTP/1.1 completely (and then that machinery could have been reused to support HTTP/3).
[0]: https://www.envoyproxy.io/docs/envoy/latest/configuration/ht...
Re: REST Servers in Go: Part 1 – standard library
#118Earlier quoted context omitted.
He means Rust. https://news.ycombinator.com/item?id=25798008
I was hoping even less that was the case, feels like a very out of touch comment, but I’m willing to listen.
Rust is hard, but it's not harder than Haskell, and at a high level of abstraction it can be simpler than Golang has to offer in the stdlib, in the happy path with better results, and more safety. Again, this is the happy path case, but it's not impossible, just hard/unlikely -- Golang on the other hand can never achieve this level of simple interfaces built on abstraction because of the goals and design choices the language has made.
The original comment is this:
> Looking at such articles makes me feel we are going back in time rather than improving efficiencies for developers to build RESTful server. If you look at Ruby on Rails you can build the server shown here in one min that is scalable and backed with database. I know people will complain about speed of execution of language and framework but do you really care if you are not expecting Google like traffic.
My point was that Rust gives you the tools to write a rails/sinatra (at a glance) library that in the happy/simple path (which most regular CRUD backends are) can be simpler than Golang because the abstractions to make it simple are there, and speed will just about always be the fastest possible relative to quality of underlying code. Golang can (and does) provide similar, but it is always a step behind (on purpose) on the abstraction front. If you're going to provide a carefully crafted interface that is very easy to use, it matters less that rust can have a really high barrier to entry (rails is valuable because you can be productive without being a ruby expert).
Re: REST Servers in Go: Part 1 – standard library
#119Defining them all on a single server struct means once you're past a handful you start having a hard-as-heck to organize folder of handlers (or a bunch of massive code files). How are folk managing Go endpoint as apps scale across number of endpoints? I'm skeptical that one struct in one folder leads to good app structure, and have seen this start to break down in a few cases in practice
It depends on what the struct contains. I have developed many Go API's professionally at several companies since Go 1.1, and all my servers and up looking like a server struct with only a few fields - a database, AWS client object, and some prometheus metrics. The logic is typically split among many files, all implementing receivers on that struct. If you have independent, different elements in that API, you break th…
Re: REST Servers in Go: Part 1 – standard library
#120Earlier quoted context omitted.
Interfaces absolutely express a type of polymorphism: any concrete type that satisfies the interface can be used in its place. What makes you think otherwise? > Essentially, what I am claiming is the Golang is a bad language for metaprogramming That's definitely true, and an explicit choice. Thank goodness!
There is no "either-or" data type in Golang. That's why. It can only be accomplished by inefficient functional hackery. In C, you just make a struct, have a type present in the struct, and then cast the struct pointer to extended object types to gain additional functionality. In this way you can easily accomplish all sorts of fun things like inheritance. Message passing type designs can easily be accomplished also in…
> Go is a bad language for metaprogramming
You’re absolutely right here.
> There is no "either-or" data type in Golang. That's why.
Correct here too, Go doesn’t have sum types. If you want sum types, you have to emulate them via interfaces. But I don’t see how that relates since all of this DI stuff seems to be dynamically typed anyway (errors at runtime) assuming you’re not taking a codegen approach anyway.
> In C, you just make a struct, have a type present in the struct, and then cast the struct pointer to extended object types to gain additional functionality.
I don’t understand what you’re trying to do here. First of all, this only works for the first field (and obviously isn’t memory/type safe).
> In Golang? Well... no. You are essentially forbidden from doing any simple casting or extension. You are essentially stuck with hardcoding the crap out of everything or making your own vcall like system build out of Golang types... which you can't really use in the way you want unless you use reflection.
As a general rule of thumb you can do almost anything in Go that you can do in C if only by delving into the unsafe package; however, “unsafe” is almost never necessary—interfaces typically suffice. You certainly can emulate inheritance if you don’t care about type-safety, just like in C. Unfortunately I can’t say more until you clarify your objective.
> What I can't understand is why anything thinks that Golang does support polymorphism. They admit it themselves. They are working on it. Only the new alpha test versions have a solution for it. The current released version is not polymorphism no matter how much you want to fucking label it that way.
I think you must mean some other word because interfaces are the canonical example of polymorphism and Go has the best interfaces in the business. :) I’ve never heard the Go maintainers claim they lack polymorphism (Go does lack type-safe generics and sum types, but so does C). In an earlier post you argued that interfaces weren’t polymorphism because they don’t let you modify the underlying data, which is patently false—this is the whole point of interfaces. In Go:
var r io.Reader // nil
r = someFile // *os.File
r = stringReader // *strings.Reader