Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion: Amazing project #6

Open
diegopy opened this issue May 4, 2020 · 9 comments
Open

Discussion: Amazing project #6

diegopy opened this issue May 4, 2020 · 9 comments

Comments

@diegopy
Copy link

diegopy commented May 4, 2020

Hi,

I've been using FastAPI for Python and now that I'm using Rust more I was looking for similar frameworks. I've been playing with hsr and so far seems pretty good. It actually uses the opposite direction than FastAPI. There you annotate your code using Python decorators and type hints and FastAPI fully generates the openapi v3 schema from it, optionally embedding a SwaggerUI in the final runtime to have nice things like "Try it out" to test the API. hsr went for the codegen approach. I'm wondering if you considered both approaches and what where the reasons that made you go for codegen. I'm not sure myself which workflow is better, although I'd like to think that having the code as the single "source of truth" and generating everything from it has some appeal.

Thanks again for this project! Looking forward to hopefully contribute when I start using it.

@adwhit
Copy link
Owner

adwhit commented May 4, 2020

Hi, thanks for your comments.

I do intend to write a section along the lines of "Why does this exist??". Have been trying to improve the documentation over last few days. When I'm happy with it I'll do some kind of announcement.

To answer your question, I too have come from the python ecosystem and over the years I have settled on connexion as my preferred way of writing APIs. This project is basically my attempt to do connexion in Rust, but with the extra benefits that Rust can give you, namely performance and correctness.

Why do I prefer to define the spec then generate code, rather than write code then generate the spec? I think these two things are very different ways of working. Using something like hsr, you get a complete framework - JSON types, server code and client code. If I used hsr in multiple projects (say in a service-oriented architecture), they would all have the same predictable structure. As long as I know how to write a spec, I do not have to bother with a lot of the tedious details of the implementation.

Also, an OpenAPI spec is language-agnostic which is very useful in a multi-language environment. In particular, for the frontend, I can use something like swagger-to-ts to generate equivalent typescript types, then I can be confident that my backend and frontend can talk to each other. I have spent so much time over the years debugging API inconsistencies, I want it to stop.

In contrast, I find little value in generating an OpenAPI spec from code, as I do not find the spec to be very useful in- and of- itself - and it doesn't free me from doing the tedious bits, namely all the server and client boilerplate. I like to use OpenAPI as a (not that great) IDL, the pretty ui rendering is nice but not really the point.

@diegopy
Copy link
Author

diegopy commented May 4, 2020

Thanks for your prompt reply.
I see your point, and it's definitely a perspective I haven't considered. Having always the same project structure generated is really valuable.
Please allow me to humbly suggest a feature that I found useful in my projects, and I'm not sure hsr supports: Complex parameter objects as specified here. OpenAPI supports passing objects encoded with json as query, path and header parameters. It's not super common but I had the need to do that in a project of mine.
Now, the million dollar question: Do you think hsr is "production ready"? Basically, are there any areas that you feel need major work before being useful?
Thanks again, and if you have a list of things that need addressing I would be happy to contribute with fixes and features implementation.

@adwhit
Copy link
Owner

adwhit commented May 7, 2020

Please allow me to humbly suggest a feature that I found useful in my projects, and I'm not sure hsr supports: Complex parameter objects as specified here

Interesting. At the moment I think only simple parameters would work (haven't tested anything more complex). The current implementation just does "whatever actix-web does" which I think relies on serde-urlencode so I would have to look more closely at how it works. I would like to support as much of the spec as possible although I think what you have suggested is relatively low priority.

Now, the million dollar question: Do you think hsr is "production ready"? Basically, are there any areas that you feel need major work before being useful?

I think the parts that are implemented 'work', and the underlying framework (actix-web) is certainly used in production. However there are still lots to do before it supports all the features that would be expected for production use. You can see an incomplete list https://github.com/adwhit/hsr/blob/master/TODO.md.

My aim is to implement everything bit-by-bit until it would be possible (at least in principal) to use the framework at my day job. The most conspicuous missing features in this regard are

  • HTTPS
  • Support for OAuth security scopes
  • Support custom configuration (i.e. adding routes that are not defined in the spec)

I would of course be delighted to accept PRs for any features in the TODO, or indeed anything else you think could be improved. Or simply giving it a try and reporting pain points would also be valuable.

BTW, this isn't really documented yet but there is a nightly-only feature pretty which runs the generated code through rustfmt before returning it. This makes error messages a LOT more readable, so if you start hacking/debugging, you'll probably want to activate that feature.

@brokenthorn
Copy link

What about error handling. I see hsr wraps async fn's that return what are basically just model objects (and not even wrapped with Result or Option). What happens when inside those fn's you encounter an error? How does that propagate up to the request handler? How do you control what error gets returned to the client?

Basically, I'm asking how would one do proper error handling with hsr at the moment?

On a side note: hsr looks like a very interesting project. I wonder why it's not more popular. I guess people prefer the official openpi/swagger codegen tools.

@adwhit
Copy link
Owner

adwhit commented Jul 15, 2020

Hi, thanks for your interest.

Error handling: is a slightly tricky one. Versions 0.1 and 0.2 allowed the user to return Result from the handlers. But I found this did not map well to the OpenAPI spec. Instead, if your endpoint can return an error, you should create an Error response type and return that. Note that you can still use Result and Option internally but the final response must be one defined in the spec (but you can use a default error response at this point for generic 'it went wrong' type errors). Take a look at the petstore example to get the idea. Incidentally this should become a lot more ergonomic if/when try blocks are stabilized.

re: popularity - while this project works fine as it stands, in my view it is not really ready for public use until the above TODO items are completed, so I have made no effort to publicise it yet. When I find the time (hopefully in August) I will add the missing features and polish it a bit, then make some kind of announcement. But in the meantime, please feel free to give it a whirl, the existing API is not likely to change much.

@brokenthorn
Copy link

I sort of always have this question with all libraries in Rust that I've tried: how does this library do error handling?

There have been so many ways to do that and will probably continue to be more than one way of doing error handling with Rust, that I wished that more crates had a small section titled "how to handle errors in X crate code".

ATM, using crates like anyhow (which strives to stay close to std idiomatic Rust) and the idiomatic (???) 'make your own error type and implement Error' practice seem to me like the best way to do errors with Rust. Seems like it's wanting to converge on that standard but I'm very new to Rust so I might not know what I'm talking about here.

What are you opinions on this? Will you consider writing a section like that, on handling errors with hsr? Will the current way change? Have you started promoting the project yet?

@diegopy
Copy link
Author

diegopy commented Aug 3, 2020

I'm not the hsr author, but my humble opinion on this as a general topic, which seems to be the consensus, is: anyhow for programs, thiserror for libraries. The thiserror crate facilitates the "Make your own error type" for library code.
If you think about it it makes sense to have different approaches to the "program" code and "library" code. A program, particularly at the top level, is concerned with handling the error, and/or propagating them where they should be handled. That's where anyhow excels.
A library on the other hand, is concerned with raising errors and, ideally, very specific errors that detail exactly what happened, and leave up to the "prorgam" using it on how to deal with it. The thiserror crate excels here.

@adwhit
Copy link
Owner

adwhit commented Aug 4, 2020

Pretty much agree with @diegopy here. For libraries, create your own precise types with thiserror. For applications, propagate errors with anyhow.

However, for this particular framework, error types will be defined by the user via the spec, so it isn't necessary to include library errors beyond that (at least in principle).

re: progress, haven't worked on this recently but it is still on my radar, just need to find a solid uninterrupted week or so to get it polished.

@brokenthorn
Copy link

I kind of switched my focus to async-std, tide, async-h1 and that side of the Rust HTTP async-await server story.
Otherwise, I would have loved to try hsr and maybe even contribute. Right now I decided on HTTP RPC-style for my current project and have already started working.

Still an amazing project, at least in principle. I do wish it a lot of luck and success and I hope it will be a good fit for most people doing Rust web APIs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants