Hacker News new | ask | show | jobs
by mdasen 821 days ago
As a curiosity, how do you feel about languages/frameworks where APIs can be pretty self-documenting? For example, Java/JAX-RS creates pretty self-documenting APIs:

    @Path("/people")
    public class PeopleApi {
        @Path("{personId}")
        @GET
        public Person getPerson(@PathParam("personId") int personId) {
            return db.getPerson(personId);
        }
    }
It's easy to generate a spec for a JAX-RS class because it has the paths, parameters, types, etc. right there. There's a GET at /people/{personId} which returns a Person and takes a path parameter personId which is an integer.

If we're talking about a Go handler which doesn't have that information easily accessible, I understand wanting to start with a spec:

    func GetPerson(w http.ResponseWriter, r *http.Request) {
        personId, _ := strconv.Atoi(r.URL.Path.something)
        person := db.GetPerson(personId)
        w.Write(json.marshal(person))
    }

    func GetPerson(c echo.Context) error { //or with something like Echo/Gin
        id := c.Param("id")
        person := db.GetPerson(id)
        return c.Json(http.StatusOK, person)
    }
In Go's case, there's nothing which can tell me what the method takes as input without being able to reason about the whole method. With JAX-RS, it's easy reflect on the method signature and see what it takes as input and what it gives back, but that's not available with Go (with the Go tools that most people are using).

This isn't meant as a Go/Java debate, but more a question of whether some languages/frameworks basically already give you the spec you need to the point where you can easily generate an OpenAPI spec from the method definition. Part of that is that the language has types and part of it is the way JAX-RS does things such that things you're grabbing from the request become method parameters before the method is called rather than the method just taking a request object.

JAX-RS makes you define what you want to send and what you want to receive in the method signature. I totally agree that people should start with thinking about what they want from an API, what to send, and what to receive. But is starting with OpenAPI something that would be making up for languages/frameworks that don't do development in that way naturally?

----------

Just to show I'm not picking on Go, I'm pretty sure one could create a Go framework more like this, I just haven't seen it:

    type GetPersonRequest struct {
        Request `path:/people/{personId}`
        PersonId int `param:path`
    }
    func GetPerson(personRequest GetPersonRequest) Person {
        return db.GetPerson(personRequest.PersonId)
    }
I think you'd have to have the request object because Go can annotate struct fields (with struct tags), but can't annotate method parameters or functions (but I could be wrong). The point is that most languages/frameworks don't have the spec information in the code in an easy way to reflect on like JAX-RS, ASP.NET APIs, and some others do.
3 comments

I absolutely hate the approach of scattering routing instructions everywhere via annotations. Nothing beats a router.go file with all the endpoints declared in the same place. Routing annotations is a bad idea that caught up just because it looks clever.

Looking for the handler for ˋGET /foo/{fooID}/barˋ is terrible in a codebase using annotations.

At work they force me to use NestJS. Want to make a new GET endpoint? Find the controller class, add a method, add a get decorator, add an authentication decorator, add a param decorator, add openapi decorators, and if you are feeling helpful, add openapi decorators to every property of every object you take in or return.

I hate decorators so much, just let me use regular data as code.

For the happy path, the Java code works great, but a good open API spec also includes the following:

- examples, they are a pain to write in Java annotations.

- multiple responses, ok, invalid id, not found, etc.

- good descriptions, you can write descriptions in annotations (particularly post Java 14) but they are overly verbose.

- validations, you can use bean validation, but if you implement the logic in code it's not easy to add that to the generated spec.

See for example this from springfox https://github.com/springfox/springfox/blob/master/springfox...

It's overly verbose and the generated open API spec is not very good.

You don't need annotations for descriptions, they get picked up from javadoc-style comments which you should have anyway. Same with asp.net.
You are right, for Spring Boot, the relatively new springdoc supports javadoc[1] as descriptions, which is better than the annotation.

[1] https://springdoc.org/#javadoc-support

your example doesn't look any worse than an openapi yaml spec given how easy/frequently you can reach 10+ identation levels for a trivial spec.

you might be able to add descriptions easily, but expressing types in yaml is much more verbose than in a decently typed language.

swaggest allows you to define your inputs and outputs, and generate docs from them.