ruby-grape / grape

An opinionated framework for creating REST-like APIs in Ruby.
http://www.ruby-grape.org
MIT License
9.89k stars 1.22k forks source link

Interest in type-checked grape? #2343

Open aleksclark opened 1 year ago

aleksclark commented 1 year ago

Heyo,

So I've been using grape extensively for the past few years, and with RBS and steep/sorbet progressing, I wondered what the interest would be in adding type-checking to grape.

In a sense, the params parsing brings maybe 30% of the benefits of types to a grape API, and provides a solid target for generating OpenAPI/Swagger. My thought would be to implement something like the following:

  1. either move params definition into RBS, or infer types from the existing DSL, preferably the latter, with the result that you get type checking/hinting/completion in your api endpoint method, e.g. params.username -> String
  2. do essentially the same thing for endpoint method returns, although those are currently just "documented" for OpenAPI purposes, but basically you should get an error if you return my_obj.to_json instead of my_obj when the endpoint is supposed to return an instance of my_obj.class
  3. Bring grape-entity along for the ride - add typing to its DSL
  4. once the above are complete, it should be possible to generate OpenAPI specs from the types themselves instead of from developer-provided doc strings/annotations, and use static analysis to ensure a given API conforms to the spec

This is obviously a fairly complex task, so my first question is, is this something people would find useful? It would eliminate a whole category of tests and bugs involved in ensuring backend talks to frontend properly, but it would also add a non-trivial amount of friction.

My second question is, where does this belong? I think monkey-patching/extending grape, grape-entity, and grape-swagger in a new gem should be possible, but I could also see an argument for forking grape and adding this functionality directly, or for trying to make it a first-class, opt-in feature of mainline grape.

Thanks for your thoughts!

dblock commented 1 year ago

I think it's a great idea, and I think you're on the right track for params. What do you think about extracting validation into a separate library (everything in https://github.com/ruby-grape/grape/tree/master/lib/grape/validations) in a way that can be swapped? You could then make a grape-params library that implements our current state, and an RBS-enabled library called grape-params-rbs. I think grape-entity and friends can follow the same model, except that I would just fork those into RBS-enabled implementations.

All that said, I would definitely POC the work by brute-forcing it in a fork first ;)

Caleb-T-Owens commented 6 months ago

I would love to see this happen

olivier-thatch commented 4 months ago

Only tangentially related to the ask in the first post, but if anyone is interested in using Grape in a Sorbet-typechecked project, you'll probably find the grape_sorbet gem helpful.

For now the gem is mostly focused on making it possible to use Grape without having to opt out of typechecking entirely with # typed: false or littering the code with T.unsafe.

In theory, it should be possible to achieve something close to what the first post suggests by defining Sorbet typed structs to hold parameters and augmenting Grape's params DSL to accept a T::Struct, but I haven't explored this at all and I'm not sure how you'd handle custom validators with this approach.

Caleb-T-Owens commented 4 months ago

Whoa! Thank you for your contributions to the space @olivier-thatch. I'll have to check this out.