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

Roadmap for Connexion 3.0 #1395

Closed
hjacobs opened this issue Jul 8, 2021 · 27 comments
Closed

Roadmap for Connexion 3.0 #1395

hjacobs opened this issue Jul 8, 2021 · 27 comments

Comments

@hjacobs
Copy link
Contributor

hjacobs commented Jul 8, 2021

While milestone 2.8.0 is primarily a maintenance/bugfix release and 2.9.0 will contain new backwards-compatible features, there are a number of open issues and PRs with ideas breaking backwards-compatibility. Connexion 3.0 will be the next major release which can introduce breaking changes. Guiding questions:

  • What do we want to support going forward? Are there parts of Connexion we should remove / no longer support?
  • What are breaking changes we need to incorporate due to new feature wishes?
  • What should be in Connexion "core" and what can be in other packages (e.g. more web frameworks)?
  • Where do we need to invest (e.g. aiohttp support)?
  • What is the overall vision for Connexion?
@RobbeSneyders
Copy link
Member

Thanks for opening the discussion @hjacobs. I would like to pick in on the overall vision for Connexion, specifically related to support of multiple frameworks.

I see 3 options on how we can handle support for multiple frameworks and think we should make a decision on how to move forward.

  1. Connexion as a web framework
    This is in line with the way Connexion currently describes itself:

    "Swagger/OpenAPI First framework for Python on top of Flask with automatic endpoint validation & OAuth2 support"

    If Connexion acts as a web framework itself, (in theory) it shouldn't matter to the user which framework is used underneath. Connexion would only need to support one sync and one async framework which is abstracted from the user. If we want to go this way we should:

    • Extend the Connexion interface to abstract as much of the underlying frameworks as possible.
    • Select an async framework to support next to Flask. Some options are:
      • aiohttp which is already partially supported and adopted by Connexion users.
      • quart which implements the same interface as Flask and partially shares it's ecosystem. This would limit the differences between sync and async and would make it easier to keep Connexion features on par between both version.
      • A framework selected based on a specific benchmark (eg. speed). Connexion can never be faster than the framework underneath, so this might be an important feature to optimize for.
  2. Connexion as a WSGI / ASGI middleware
    We could leverage existing interfaces already shared by a lot of Python web frameworks by implementing the WSGI / ASGI interfaces. This has the advantage that Connexion could be combined with any framework which also implements the interface. On the other hand, Connexion should not act as a web framework anymore, which means that it would leave some current features like resolving, routing, parameter parsing, ... up to the web framework / user.

  3. Connexion with portability layer
    This is closest to the previous discussions (Framework-agnostic Connexion #380) and implementation, where Connexion defines an interface which can be implemented for different frameworks. This option is the most flexible, but also requires the most work, since an interface needs to be implemented and maintained for each supported framework. On top of that, some features are hard to port between frameworks, and we already see that a lot of pull requests only target one framework which leads to 'feature drift'.

A possible 4th option would even be to be able to run both as middleware with limited features, or as web framework with full feature set.

Looking forward to hear some other views / input on this.

@flyte
Copy link

flyte commented Jul 27, 2021

I've enjoyed using connexion in the past, and I've arrived at this issue after looking for a way to write an OpenAPI-first API which is able to interact with AnyIO or Trio. I mention this because one of your options is to sit on top of aiohttp, which would (AFAIK) limit the user to the asyncio event loop.

@Ruwann Ruwann pinned this issue Aug 2, 2021
@RobbeSneyders
Copy link
Member

@hjacobs @Ruwann it would be great to get your view on this.

@thanhlim
Copy link

thanhlim commented Sep 2, 2021

I really like this package and its intent. I think it would be great if it was framework agnostic. Or at least have plugins that people could develop to slot into connexion where others can easily write the plugin.

It would make this package really useful as frameworks change quite a bit, and that freedom to compose it all would be amazing.

@Ruwann
Copy link
Member

Ruwann commented Sep 5, 2021

@RobbeSneyders Thanks for the comprehensive comment.

As you said for option 2, this would limit connexion to checking whether requests and responses adhere to the defined schemes in the Swagger/OpenAPI contract and drop functionality such as the automatic routing based on the spec. I don't think this is the best option for connexion, but curious to see if others see it in a different way.

What I like about option 3 is that the user is able to still choose the framework they prefer, and have the nice stuff from connexion. They could then also leverage some framework-specific things, which connexion doesn't have to be aware of.
However, I believe it could be very hard to have a complete framework-agnostic layer. As you say, this requires an interface for each framework, which will lead to feature drift between frameworks, as already seen.

That said, I do like the appeal of being able to leverage the strengths of certain frameworks, which is not possible with option 1. This would make it easier to maintain, but perhaps also limit the usability of connexion. So, I'm wondering whether it makes sense to consider a scenario in which we allow connexion to be more framework-agnostic, 'officially' support a very limited of frameworks (flask, aiohttp, quart for example) and then have some 'non-official' frameworks as backends. This would not force use to keep 100% feature parity between frameworks, but it would still be possible to use a different framework if desired.
However, I'm not sure whether these other frameworks would be useful enough to be used if they are perhaps not actively maintained here on the connexion repo.

@zwing99
Copy link

zwing99 commented Sep 7, 2021

I love this discussion as it gets to the heart of the "whys" when we pick a framework we use. It is often a set of tradeoffs and value adds the optimize for what we want. I work at a company that largely uses AWS Lambda for hosting its APIs. We happen to be using Kong to invoke a Lambda and differing the routing to the Lambda itself. A framework like Connexion seems like an interesting way to map an openapi spec to a function in the lambda to call upon hitting the API. The main challenge of using lambda based APIs comes from the slow startup (initialization of objects); this has steered me away from using flask inside of a lambda and go with a lighter weight framework like bottle. All that being said I wonder if there is an opportunity to adapt a framework like Connexion to be that lightweight router that takes a payload given to lambda by Kong or AWS API Gateway and route to a function. If the init time is fast enough I could see this being a powerful framework for those want to use Lambda APIs which can have significant cost benefits. Thought I would throw this in as food for thought as you take the next steps with the project.

@thanhlim
Copy link

thanhlim commented Sep 7, 2021

@zwing99 I use Connexion and Lambda myself, but using serverless and API Gateway. How's the startup because it seems like there isn't that large of a difference, especially if the lambda is already provisioned?

How much extra latency did you see running Flask versus bottle overall?

Either way, do we have full Flask 2.0 integration? I've not run the async ones as a test yet.

@TiemenSch
Copy link

Ah, this is great news!

I was about to jump ship over to FastAPI, but would have had to drop the "spec first" approach that initially drew us to Connexion. For my use case, the performance isn't that much of an issue, but "out-of-the-box" and partial automation experience is.

Major points of improvement would be the spec (and input/output) validation, which is a source of some headache with it's output for longer specs of more involved APIs in recent versions. If I'm not mistaken, there are some other more dedicated OpenAPI spec parsers available in Python, which could offload that code to a single dependency.

Also, the automated middleware is a huge plus that helps reducing duplicative code. (translating endpoints to Python methods using a RestyResolver in our case).

That said, option 3 would be awesome, but could be gradually worked towards while maintaining option 1 and extending that to support the async framework.

@RobbeSneyders RobbeSneyders unpinned this issue Dec 13, 2021
@Ruwann Ruwann pinned this issue Feb 12, 2022
@mfrnd
Copy link

mfrnd commented Mar 5, 2022

I came here after investigating WSGI. Currently, I'm using the following stack:

  1. CherryPy / Cheroot WSGI server, depending on requirements
  2. Custom WSGI middlewares
  3. Flask
  4. Connexion

This stack can be extended on top of the WSGI server as needed, e.g.:

  • other WSGI middlewares
  • WSGI web frameworks in parallel with Flask by using a different mount path (e.g. Django)
  • Extend Flask application with custom content

So, I was quiet impressed of how well this setup works together :) I cannot contribute much, just some outsider thoughts:

  • The primary reason I picked Connexion was due to the API-first approach, getting the Flask foundation is a lucky side effect, having WSGI-compatibility in the stack is an enabler.
  • Strengthen the strengths: API-first on top of a widely adopted sync and async framework
  • Stay up-to-date: OAS v3.1

@RobbeSneyders
Copy link
Member

Thanks for all the feedback everyone.

@Ruwann and I had some offline discussions about this, and we believe we've come up with an approach that's a good balance between the options above. It will greatly increase the number of supported frameworks, while reducing the framework-specific maintenance effort.

Currently, Connexion works by injecting its functionality in between the underlying framework app and the user view function, by wrapping the view function in decorators. (See our ARCHITECTURE.rst).

We propose to move most of this functionality to ASGI middleware, which is wrapped around the framework app. This allows this functionality to be framework agnostic, and actually work with any ASGI (or WSGI by using an adapter) compliant framework. This means that you can add the power of Connexion to existing apps of any of these frameworks, without Connexion having to maintain a framework-specific interface.

image

The only functionalities that cannot be moved to this framework agnostic middleware are the resolver to automatically map operations to python functions, and the automatic parameter unpacking which needs to remain a decorator since it needs to have access to the view function.

To keep providing this functionality, we will keep offering framework-specific Connexion apps, which can be wrapped in the middleware for full Connexion functionality. We will offer two of these apps: a Flask app which is synchronous by default and offers support for Flask 2.X async routes, and a lightweight ‘native’ Connexion app which is asynchronous by default and will offer support for sync routes.

Unfortunately, we’ll have to drop aiohttp support, since it is neither ASGI nor WSGI compliant. We feel however that the trade-off is worth it to become compatible with almost all other popular Python web frameworks. We’re eager to hear from our aiohttp users about which features the native async app should support to make the transition as easy as possible.

The interface that needs to be implemented for these framework-specific apps will shrink due to functionality moving to the middleware, so it will become easier to implement it for multiple frameworks. However, we would like to postpone any decision on this until we have a better view on the actual remaining interface, and the additional value that such implementations would still provide.

I've implemented a proof of concept that shows this middleware concept in #1477.

Next to this, we would like to work on the following changes:

  • Update and improve documentation
  • Drop Flask 1.X and support Flask 2.0 async routes
  • Drop Python 3.6
  • Make validation pluggable by content type
  • Make security pluggable by authentication type
  • Add typing and mypy checks
  • Support OpenAPI 3.1
  • Improve oneOf support
  • Drop automatic json serialization if content type is not set
  • Change readOnly behavior (400 error for readOnly=True #942)
  • Drop uwsgi metrics support

How much of these changes we'll be able to complete for version 3.0 depends on how many people will contribute, so we might not be able to include all of them in the end. But we'll try to create a clear roadmap to support dividing the work.

Looking forward to your feedback, and to start working on this!

@cognifloyd
Copy link
Contributor

cognifloyd commented Mar 11, 2022

It will greatly increase the number of supported frameworks, while reducing the framework-specific maintenance effort.

Cool! Which frameworks? I've heard good things about a variety of async frameworks, but haven't pursued them because they're not supported by connexion. 😉

Unfortunately, we’ll have to drop aiohttp support, since it is neither ASGI nor WSGI compliant.

I do not want to use Flask. All of my contributions to connexion have been improvements to the aiohttp support.

Could something like aiohttp-asgi allow connexion to continue supporting aiohttp? (I haven't used aiohttp-asgi, it was just one of the first results in a quick web search)

We’re eager to hear from our aiohttp users about which features the native async app should support to make the transition as easy as possible.

  • spec-first! (this one goes without saying, but I said it anyway 😉 ).
  • asyncio-based (ie: I don't want to use trio or tornado)
  • not flask (my un-reasoned bias)
  • fast

As far as my contribution outlook goes: I'm itching to replace some very NIH/custom api frameworks with connexion. I'm not in a place where I can do that with the current projects I'm working, so I don't know when I'll be able to pick up and help connexion again. That said, I'm so excited to see all the recent progress!

@RobbeSneyders
Copy link
Member

Cool! Which frameworks?

Every framework that is ASGI or WSGI compliant. Some notable examples are Bottle, Blacksheep, Django (channels), Falcon, Flask, Quart, Sanic, and Starlette.

Could something like aiohttp-asgi allow connexion to continue supporting aiohttp?

Unfortunately it won't, as it doesn't add an ASGI interface to aiohttp, but provides support to run an ASGI app within an aiohttp app.

spec-first! (this one goes without saying, but I said it anyway wink ).
asyncio-based (ie: I don't want to use trio or tornado)
not flask (my un-reasoned bias)
fast

Thanks for the input. I've currently implemented the middleware PoC using Starlette (as a toolkit, not a framework) and anyio, which works on top of either asyncio or trio. We're currently thinking of using the same stack to build the native async app.

@RobbeSneyders
Copy link
Member

I would propose to release 2.13 as a final 2.X version with the current changes, and start working towards 3.0 on main. We can create a separate v2 branch to release patches until 3.0 is ready. WDYT @Ruwann?

@Ruwann
Copy link
Member

Ruwann commented Mar 21, 2022

Yes, agreed. Let's keep supporting 2.x on a separate branch and start development for 3.0 on main

@vmarkovtsev
Copy link
Contributor

Well, my company has to fork 2.x branch. We are using aiohttp and are not going to drop it any time soon. Perhaps, aiohttp 4.x will become ASGI/WSGI compliant, and the fork will be obsolete then. Meanwhile, I will throw away other backends and embed all the current monkey patches improving the performance and adding new features into the forked codebase. Once we come up with a good name, I'll post a link.

@RobbeSneyders
Copy link
Member

Could you share which aiohttp specific features you're using on top of Connexion @vmarkovtsev?

@vmarkovtsev
Copy link
Contributor

Sure. Our project is proprietary but public: https://github.com/athenianco/athenian-api/blob/master/server/athenian/api/connexion.py

What comes to my mind straight away:

  • HTTP client.
  • Middlewares.
  • A few extra routes outside of the spec.
  • RW stuff in request.app["key"].
  • Changed the request size limit.
  • Signal handling and the graceful shutdown sequence.
  • Adapted Prometheus to expose the aiohttp-dependent metrics.
  • Tuned the Sentry integration to make a few things appear better.
  • pytest-aiohttp and related unit tests.

@zwerheim
Copy link

I've only come back to discover the connexion handoff from Zalando today.

I am in a similar boat compared to @vmarkovtsev. We use aiohttp and there is no practical possibility that we will move away from it. It will likely be easier to drop our use of connexion, than it would be to switch to flask. We cannot to maintain our own fork of Connexion.

When we looked at web framework we wanted an asyncio based HTTP server that also supported sqlalchemy out of the box. At the time, aiohttp was one of the only contenders.

We were able to re-use a lot of code from other projects instead of rewriting the entire data layer.

After that decision we started to use a feature list very similar to the above:

  • middleware
  • aiohttp specific session handling
  • sentry
  • aiohttp unittests

@vmarkovtsev
Copy link
Contributor

Our fork is here: https://github.com/athenianco/especifico

It's not really functional atm, but we are going to put it in good shape this week.

@RobbeSneyders
Copy link
Member

Thanks for the info.

The most logical switch from aiohttp would not be to Flask, but to the new async app or one of the ASGI compatible frameworks, which provide a lot of similar functionality. The middleware architecture will of course also allow custom and existing middleware (such as Sentry, Prometheus, ...) to be added.

That being said, happy to see an aiohttp-based fork of v2.

@kornicameister
Copy link
Contributor

Some longer time ago I had a dream to play around with building API first project on top of aiohttp. Never gotten around to anything functional but if anyone would want to pick up where I left of I am happy to help.

https://github.com/kornicameister/axion

@ddurham2
Copy link
Contributor

ddurham2 commented Jul 5, 2022

Heavy user of aiohttp+connexion here...

I was a bit surprised to notice this issue, but as long as ...

... then I'll remain a happy camper. Only, notice that those tests, which fixed legitimate general behavioral bugs, were added only to the aiohttp testing files. I am unsure if they were bugs in flask too but there was at least one report that that one was (#992).

@rashmi-mi
Copy link

What is the current status of the connexion.middleware package? Is it expected to be released anytime soon to be used with an ASGI framework like starlette and uvicorn?

@RobbeSneyders
Copy link
Member

It's in progress 🙂

#1610 moves the last piece of functionality to the middleware stack. I've been working on the async app and sharing the parameter decorator with it for a while locally. I hope to submit a PR for it still this year.

After that, I hope we can create some first alpha releases while we work on the docs, async tests, ...

@rashmi-mi
Copy link

Thanks. We have been using Connexion with Flask but looking to move to an ASGI server and framework like Starlette or FastAPI but want to continue using connexion. Would love to test the alpha release. :)

@smit-mehta25
Copy link

smit-mehta25 commented Jan 30, 2023

I have been using the connexion library for many years with the flask framework, I would like to highlight a few of the difficulties I have faced while using this package.

  • Support for splitting large YAML files into small files. Basically, we have aggregated the APIs based on the base_path and created multiple YAML modules. and, a main YAML for the end-points that are unique. later on, we faced performance issues as parsing YAML files (14-16 YAML modules) using prance was taking a huge time. So, we have written a script that will again merge the compiled/resolved specifications (prance.ResolvingParser(spec).specification) into a single JSON module. (we used the deepmerge lib to merge all resolved specifications) since prance resolves the references and the output is serialized into the dictionary we have to use the JSON module. The performance issues are still there as the connexion uses the yaml.safe_load() to load the specification from JSON modules.

In short, the connexion library should support _load_spec_from_json_module using json.load() or provide a way to use custom parse since the performance of each parser is different. Also, the argument param does not work with the specification passed as a dictionary while using connexion_app.add_api method

the process of merging all the compiled/resolved specifications into a single entry-point will be done before the container builds. to save processing time. and once the JSON module is created the connexion_app instance will consume the JSON module.

@hjacobs @RobbeSneyders FYI,
for more details please go thorugh the below issue.
#1629

@RobbeSneyders
Copy link
Member

We published a first alpha version of Connexion 3 🎉

We'd love to hear your feedback while we further polish the code and update the documentation.

For more info and further discussion, see the announcement.

@RobbeSneyders RobbeSneyders unpinned this issue Mar 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests