Coder Social home page Coder Social logo

fastapi-versioning's Introduction

fastapi-versioning

api versioning for fastapi web applications

Installation

pip install fastapi-versioning

Examples

from fastapi import FastAPI
from fastapi_versioning import VersionedFastAPI, version

app = FastAPI(title="My App")


@app.get("/greet")
@version(1, 0)
def greet_with_hello():
    return "Hello"


@app.get("/greet")
@version(1, 1)
def greet_with_hi():
    return "Hi"


app = VersionedFastAPI(app)

this will generate two endpoints:

/v1_0/greet
/v1_1/greet

as well as:

/docs
/v1_0/docs
/v1_1/docs
/v1_0/openapi.json
/v1_1/openapi.json

There's also the possibility of adding a set of additional endpoints that redirect the most recent API version. To do that make the argument enable_latest true:

app = VersionedFastAPI(app, enable_latest=True)

this will generate the following additional endpoints:

/latest/greet
/latest/docs
/latest/openapi.json

In this example, /latest endpoints will reflect the same data as /v1.1.

Try it out:

pip install pipenv
pipenv install --dev
pipenv run uvicorn example.annotation.app:app
# pipenv run uvicorn example.folder_name.app:app

Usage without minor version

from fastapi import FastAPI
from fastapi_versioning import VersionedFastAPI, version

app = FastAPI(title='My App')

@app.get('/greet')
@version(1)
def greet():
  return 'Hello'

@app.get('/greet')
@version(2)
def greet():
  return 'Hi'

app = VersionedFastAPI(app,
    version_format='{major}',
    prefix_format='/v{major}')

this will generate two endpoints:

/v1/greet
/v2/greet

as well as:

/docs
/v1/docs
/v2/docs
/v1/openapi.json
/v2/openapi.json

Extra FastAPI constructor arguments

It's important to note that only the title from the original FastAPI will be provided to the VersionedAPI app. If you have any middleware, event handlers etc these arguments will also need to be provided to the VersionedAPI function call, as in the example below

from fastapi import FastAPI, Request
from fastapi_versioning import VersionedFastAPI, version
from starlette.middleware import Middleware
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI(
    title='My App',
    description='Greet uses with a nice message',
    middleware=[
        Middleware(SessionMiddleware, secret_key='mysecretkey')
    ]
)

@app.get('/greet')
@version(1)
def greet(request: Request):
    request.session['last_version_used'] = 1
    return 'Hello'

@app.get('/greet')
@version(2)
def greet(request: Request):
    request.session['last_version_used'] = 2
    return 'Hi'

@app.get('/version')
def last_version(request: Request):
    return f'Your last greeting was sent from version {request.session["last_version_used"]}'

app = VersionedFastAPI(app,
    version_format='{major}',
    prefix_format='/v{major}',
    description='Greet users with a nice message',
    middleware=[
        Middleware(SessionMiddleware, secret_key='mysecretkey')
    ]
)

fastapi-versioning's People

Contributors

bunjiboys avatar deanway avatar dependabot[bot] avatar gtlambert avatar hackancuba avatar kamyar avatar nicholascm avatar patrickelectric avatar rafaellehmkuhl avatar rohithill avatar simon-castano avatar synchronizing avatar tijptjik avatar zenahr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastapi-versioning's Issues

dependency_overrides cannot be used in tests as expected, documentation lacking

Describe the bug

Without fastapi-versioning, dependency_overrides can easily be specified on the app contained in the TestClient a la (pytest fixture like pseudo code):

before = client.app.dependency_overrides
client.app.dependency_overrides = dict(overrides)
try:
    yield
finally:
    client.app.dependency_overrides = before

With the internal use of sub application mounts, this doesn't work anymore and the override never reaches one of the versioned sub applications.

To Reproduce
Steps to reproduce the behavior:

from fastapi import APIRouter, Depends, FastAPI
from fastapi.testclient import TestClient
from fastapi_versioning import versioned_api_route, VersionedFastAPI

app = FastAPI(title="test-app")
router = APIRouter(route_class=versioned_api_route(1, 0))

def dependency() -> str:
    return "original"

@router.get("/test")
def get_test(dep: str = Depends(dependency)) -> str:
    return dep

app.include_router(router)
app = VersionedFastAPI(app)


def test_this() -> None:
    client = TestClient(app)
    client.app.dependency_overrides = {dependency: lambda: "patched"}
    assert client.get("/v1_0/test").json() == "patched"

This test will fail

Expected behavior

It should at least be documented how to access the correct sub application for providing the dependency_overrides.

Versioning removes openapi tags / tag metadata.

Describe the bug
Firstly a HUGE thanks for this.
Using tag metadata as per Metadata and Docs URLs
When implementing VersionedFastAPI, original openapi tags / tag metadata disappear from openapi.json

To Reproduce

from fastapi import FastAPI
from fastapi_versioning import VersionedFastAPI
import uvicorn

tags_metadata = [
    {
        "name":"root",
        "description":"Root description here" 
    }
]
original_app = FastAPI(title="Items API", description="API for Items in the database.",  openapi_tags=tags_metadata)
@original_app.get("/", tags=["root"])
def root():
    return {"root":"hello"}

versioned_app = VersionedFastAPI(original_app, version_format='{major}', prefix_format='/v{major}', enable_latest=True, openapi_tags=tags_metadata)

if __name__== "__main__":
    uvicorn.run(original_app, host="0.0.0.0", port=8000)
    # uvicorn.run(versioned_app, host="0.0.0.0", port=8000)

If you run uvicorn.run(original_app, host="0.0.0.0", port=8000), Root description here appears in openapi.json as expected.
If you run uvicorn.run(versioned_app, host="0.0.0.0", port=8000), Root description here disappears from openapi.json

Expected behavior
Expected openapi tags to carry over to versioned api.

Additional context
Thanks for the versioning module - aside from this issue it has been awesome!

root_path is not propagated to versioned apps properly, breaking openapi docs

My app is behind some proxy so it's url is smth like http://abc.cd/some/path/v1_0/ to support it properly I'm adding root_path="/some/path", and in general everything is working ok as in most cases this param is just ignored, except openapidocs, where it's used to generate path to openapi.json:

http://abc.cd/some/path/v1_0/docs

    const ui = SwaggerUIBundle({
        url: '/v1_0/openapi.json',

should be:

    const ui = SwaggerUIBundle({
        url: '/some/path/v1_0/openapi.json',

Events not working if put before VersionedFastAPI initialization

Actual behavior
I have events on startup and shutdown. When I put these configs before VersionedFastAPI initialization, the events does not working.

# App Event Setting
@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

# versioning
app = VersionedFastAPI(app,
    version_format='{major}',
    prefix_format='/v{major}',
    enable_latest=True)

Expected behavior
I hope it still working even all Events setting put before VersionedFastAPI initialization.

AttributeError: 'APIWebSocketRoute' object has no attribute 'methods'

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
Yes, I want to use this with a api with Websockets as well
Describe the solution you'd like
A clear and concise description of what you want to happen.
Websocket support in fastapi versioner
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
I right now have my own middleware to handle these things but this looks better
Additional context
Add any other context or screenshots about the feature request here.

Warning: "openapi_prefix" has been deprecated in favor of "root_path"

First of all thank you for your work on the module! I really like what you are doing and its working great.

Describe the bug
During startup of the API a warning message is shown (likely due to a change in FastAPI):
"openapi_prefix" has been deprecated in favor of "root_path", which follows more closely the ASGI standard, is simpler, and more automatic. Check the docs at https://fastapi.tiangolo.com/advanced/sub-applications-proxy/
Might be a simple change here ?

To Reproduce
Minimal example:

#example.py
import uvicorn
from fastapi import Depends, FastAPI
from fastapi_versioning import version, VersionedFastAPI

app = FastAPI()

@app.get(path='/example')
@version(1, 0)
def example():
    return 'test'

app = VersionedFastAPI(app) #here the warning is shown

if __name__ == "__main__":
    uvicorn.run("example:app",host="127.0.0.1",port=8000,reload=True,debug=True)

Warning message:
Anmerkung 2020-07-07 144511

Not related to the bug
I was happy to find that module is even doing more than you write in the readme: If a route is only specified in version v1_0 and not overwritten by a new version it is also available in newer endpoints (e.g. v2_0). For me this seems worth mentioning in the readme ๐Ÿ‘

VersionedFastAPI causes all OPTIONS requests to return a 405

Describe the bug
When using VersionedFastAPI all OPTIONS requests return a 405.

app = FastAPI(title='APP NAME')
APP_VERSION = "1.1.1"
app.add_middleware(
    CORSMiddleware,
    allow_origin_regex='https?://.*',
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*'],
)
app.include_router(api_router_v1_0, prefix=config.API_V1_STR)
app.include_router(api_router_v1_1, prefix=config.API_V1_STR)
app = VersionedFastAPI(app)

To Reproduce
Steps to reproduce the behavior:

  1. initialize app using VersionedFastAPI
  2. make a request that requires CORS options
  3. view response details
  4. returns a 405

Expected behavior
Options request to pass as expected

Router with versioning

Is your feature request related to a problem? Please describe.
Currently from the examples I've seen to have versioning on multiple router you need to proceed as such. Where you will need to create different router and append the versioning numbers yourself without using the @version decorator. Is there a more clear cut way to do this without creating different routers and use the version decorator

Describe the solution you'd like
I would like to be able to version my routers similar to the examples in the README, so something like:

from fastapi import APIRouter
from fastapi_versioning import VersionedFastAPI, version

router = APIRouter(
    prefix="/greetings",
    tags=["greetings"],
)

@router.get("/hello")
@version(1)
def greet_with_hello():
    return "Hello"


@router.get("/hello")
@version(2)
def greet_with_hi():
    return "Hi"

router= VersionedFastAPI(router)

Describe alternatives you've considered
I can proceed with the example listed in Issue 34. However, manually adding the version number to the url is something I would like to avoid.

Additional context

Allow custom versioning format

Is your feature request related to a problem? Please describe.
I want to custom version format like: v1.0, version_1.0

Describe the solution you'd like
We should allow to custom following parameters:

  • Version prefix: v (default), version_ (custom)
  • Version separator/delimiter: _ (default), . (custom)

Versioning not applied to RedirectResponse

Describe the bug
A clear and concise description of what the bug is.

When redirecting from a versioned route to another I'd expect the same version to be prefixed, but that's not happening.
As a fallback I'd expect a way to fetch the current version, but there seems to be no easy way to do that (I guess I can fetch it from current path, but doesn't seem clean).

To Reproduce
Steps to reproduce the behavior:

@version(1, 0)
@app.post("/original_route")
async def original_route():

    return "test"

@version(1, 0)
@app.post("/route_alias")
async def redirect_search_ads():

    return RedirectResponse(
        '/original_route', 
        status_code=status.HTTP_302_FOUND)


app = VersionedFastAPI(app,
    version_format='{major}.{minor}',
    prefix_format='/v{major}.{minor}')

Expected behavior
Redirect from /v1.0/route_alias to /v1.0/original_route instead of to /original_route

Generate the openapi_schema for a specific api version using the app.openapi() method

Is your feature request related to a problem? Please describe.
Hey, I'm trying to generate the full openapi.json file for a specific API version without running the server, programmatically, as I need to generate the html documentation from it. Is there a way to do that? using the app.openapi() would hide the endpoints behind the versions.
But I need the openapi schema for a specific API version.

Describe the solution you'd like
A method that would allow the generation of a specific versioned openapi schema, something like app.openapi('v1') or anything would work really.

Additional context

from fastapi.openapi.utils import get_openapi
from app import app
import json


with open("openapi.json", "w") as file:
    json.dump(app.openapi(), file)


with open("openapi-get.json", "w") as file:
    openapi_schema = get_openapi(
        title="Custom title",
        version="2.5.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )

The generated openapi schema includes the v0 and v1 endpoints, however need a method to generate the schema with the endpoints for a specific version.

Change naming convention of api version in url

Is your feature request related to a problem? Please describe.
In the generated versioning v1_0, v1_2. The underscore is unintuitive when it comes to api versioning.

Describe the solution you'd like
Change the underscore to a dot.

Describe alternatives you've considered
None

Additional context
None

Maintained Fork Inquiry

Are there a good number of people interested in this still? Could potentially fork this and address some of the issues here based off how many people would want that. I see it hasn't been maintained in over a year.

What is the status of this project? Is it still active?

Hello,

First of all, thank you for the project.
I would like to ask if the project is still active. I can see some PRs opened since last year and I was planning to open a PR but in case there is no activity I probably going to create a new project instead of a fork, of course giving appropriate credit.
Thank you in advance for the response

Support for "latest" version?

We have a use case where our API is decoupled from services that call it, which would require another lengthy PR process for our monolith application to update the client to use the newer version of the API.

It would be great to be able to call /api/latest/endpoint or /api/v/latest/endpoint to automatically use the latest version of the API. This would allow the monolith client code to not have to be changed when we add a new minor/major version to our API.

ENDPOINTS not showing in docs page after enabling versioning

Describe the bug
Once i enable endpoint versioning, url with version works fine in postman testing but on docs endpoints are not showing.

To Reproduce
Steps to reproduce the behavior:
define FastAPI app
define endpoints with @Version()
define VersionedFastAPI

Expected behavior
see endpoints in docs(Swagger UI) with two versions

Avoid defining own dunder property(s)

Is your feature request related to a problem? Please describe.
In code used dunder property __api_version__ for function.

Describe the solution you'd like
Just set the _api_version.

Describe alternatives you've considered
This looks more pythonic, than your idea.

kwargs is not passed to "subapps" constructor in the "version_route_mapping" loop.

Describe the bug
The function "VersionedFastAPI" is receiving the "kwargs", but this is not passed to the constructor of each API Version in the "version_route_mapping" loop. This does not allow that the apps' versions have the same properties that the parent app.

Steps to reproduce the fix:

  1. Go to the function versioning.VersionedFastAPI, and add the kwargs in the FastAPI constructor.

Bug for exception handler

Describe the bug
When trying to define global exception handler, catching user defined exception fails with error "Caught handled exception, but response already started."

def custom_exception_handler(
request: Request, exception: CustomNotFoundError
) -> JSONResponse:
return JSONResponse(
status_code=status.HTTP_404_NOT_FOUND, content={"message": f"Oops! {exception.name}"}
)

exception_handlers = {CustomNotFoundError: custom_exception_handler}

app = FastAPI(
title="Root app",
version="0.1.0",
exception_handlers=exception_handlers,
)
app.include_router(api_router)
app = VersionedFastAPI(
app,
version_format=f"{MAJOR_PLACEHOLDER}",
prefix_format=f"/v{MAJOR_PLACEHOLDER}",
default_version=(DEFAULT_MAJOR_VERSION, DEFAULT_MINOR_VERSION),
exception_handlers=exception_handlers,
)

To Reproduce
Steps to reproduce the behavior:

  1. initialize app using VersionedFastAPI
  2. make a request to service that raise user defined exception CustomNotFoundError
  3. returns a Http status 500 with error "Caught handled exception, but response already started."

Expected behavior
Global exception handler should catch CustomNotFoundError exception and return HTTP_404_NOT_FOUND with message

Additional context
**kwargs, should be passed to Fastapi in the constructor of VersionedFastAPI like below:
versioned_app = FastAPI(
title=app.title,
description=app.description,
version=semver,
openapi_prefix=prefix,
**kwargs,
)

Extend documentation in using middleware with versioning

I recently found out that the use of middleware has to be added to the versioning_app otherwise it will not work.
Should be added to the documentation

import time
from fastapi import FastAPI, Request
from fastapi_versioning import VersionedFastAPI, version

app = FastAPI(title='My App')

@app.get('/greet')
@version(1, 0)
def greet():
  return 'Hello'

@app.get('/greet')
@version(1, 1)
def greet():
  return 'Hi'

app = VersionedFastAPI(app)


@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    """Middleware to set response time."""
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response

"Latest" endoint just catching `GET` requests

Describe the bug
The recently added /latest endpoint is redirecting only GET requests. POST and other types of requests are not accepted.

Expected behavior
All types of requests being redirected.

Transformation-based versioning

Is your feature request related to a problem? Please describe.

Maintaining multiple API versions explicitly in the code base results in higher maintenance costs with each additional version that is kept alive. This blog post by stripe explains an interesting idea of countering this fact by representing versions via transformations to the responses instead, such that the code base, apart from declaring the transformations, only has to deal with the most recent API versions and all other versions are implicitly generated through the chained application of transformations.

Describe the solution you'd like

It would be cool if fastapi-versioning (or potentially another module) would realize a transformation-based approach to recude the long-term costs of maintaining multiple versions in parallel.

Additional context

I could find an implementation of this pattern for the django rest framework: https://github.com/mrhwick/django-rest-framework-version-transforms/tree/dev/tests. This could serve as an inspiration.

Is there any way to versioning the internal methods like services, db operations, helpers etc

This package seems very good for API route versioning, is there any way to versioning the internal methods like services, db operations, helpers etc

@version(1, 0)
@staticmethod
def get_seasons():
        return {
                'summer': 'June-August',
                'winter': 'December-February',
                'autumn': 'September-November',
                'spring': 'March-May'
        }
@version(1, 1)
@staticmethod
def get_seasons():
        return {
                'summer': '6-8',
                'winter': '12-2',
                'autumn': '9-11',
                'spring': '3-5'
        }

So in future if i need to remove the old version of service, helpers or db methods I can easily find by version
and remove unused codes.

remove "latest" keyword from url path for the latest version API?

Is your feature request related to a problem? Please describe.
This is a question regarding #35.
I am curious about why you would want the keyword "latest" to be in the path, because it feels more natural that /api/endpoint serve the latest version of the API by default.

Describe the solution you'd like
If that is clear, i guess we can remove the keyword "latest" from the prefix in versioning.py, line 76, so anything from the default API direct to the latest API.
Reference source code link:

modification:

if enable_latest:
        prefix = "/"
......

Alternative option
The above question is actually from the concern whether I could set some version of API to be a generic API like...
suppose you have v1, v2, and v3 APIs where all have /items endpoint, and you want v2 to be accessible by default path like /items ( /items redirect to /v2/items )

Additional context
I would mainly want to know the decision behind the usage of the keyword; #35 describes the reason, but it's not really convincing to me and if the concern here is about serving the latest version to client, generic API call without any version, keyword, etc would be better for users and also for restfulness too.

Add option to deprecate paths

Is your feature request related to a problem? Please describe.
Once defined, all paths are added to the api version they are linked with, and all the following versions after that. It would be beneficial to be able to deprecate paths

A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A deprecate_in= option in the @version decorator could cause certain viewfunctions to stop being added to the API paths after a certain version.

An example would be the following

@app.get('/greet')
@version(1)
def greet():
  return 'Hello'

@app.get('/greet')
@version(2, deprecate_in=3)
def greet():
  return 'Hi'

@app.get('/new-greet')
@version(3)
def new_greet():
  return 'This is a different greetings function linked to a different url'

This would result in the following endpoints:

/v1/greet
/v2/greet
/v3/new-greet

(Now, the normal behaviour without deprecate_in is that in v3, both the /v3/new-greet and /v3/greet paths would be available)


I have implemented the solution, but I cannot push it. Can I be given access?

Versioning causes FastAPI-Redis plugin to malfunction

Describe the bug
As soon as I activate VersionedFastAPI the Redis plugin throws an error in depends_redis() function. Seems like Redis is not properly initialized. The Redis object simply is not there when depends_redis wants to return it. The startup function is called normally and the Redis object is initialized but in a different version of the FastAPI app that is not used in versioned routes. I've added more comments to the gist posted below.

To Reproduce
Here's a minimal code snipped to reproduce the problem:
https://gist.github.com/fusion44/8b9867e13a03edf29388f6b9c9e4b8ca

Expected behavior
Redis to be available on any depends call.

Thank you.

Support for a single method to handle multiple versions

Currently, if we are to support multiple versions of an API with many endpoints, we need to duplicate each endpoint (even if logic does not change for every endpoint) in order to be backward compatible.

An array of values could be passed to @version() to allow a single method to support multiple versions. Example:

Current:

def handle_home() -> str:
    return "You are home."

@app.get("/home")
@version(1, 0)
def home() -> str:
    return handle_home()

@app.get("/home")
@version(1, 1)
def home() -> str:
    return handle_home()

@app.get("/home")
@version(1, 2)
def home() -> str:
    return handle_home()

Suggested:

@app.get("/home")
@version([1, 0], [1, 1], [1, 2])
def home() -> str:
    return "You are home"

Using versioning is breaking the tags_metadata in the generated docs

Describe the bug
Using the versioning breaks some of the tags_metadata functionality .

To Reproduce
Steps to reproduce the behavior:

  1. create a small app:
tags_metadata = [
    {
        "name": "users",
        "description": "Operations with users. The **login** logic is also here.",
    },
]


app = FastAPI(openapi_tags=tags_metadata)

@app.get("/users/", tags=["users"])
@version(1)
async def get_users():
    return [{"name": "Harry"}, {"name": "Ron"}]

app = VersionedFastAPI(
    app,
    version_format="{major}",
    prefix_format="/api/v{major}",
)

2.run it and check the generated documentation at:
/api/v1/docs

Expected behavior
Notice that above the get_users method you will see the expected tag
"Users"
but the description will not be displayed.
The expected behavior obtained if versioning is not used is that both the Tag and the Description will be displayed:
"Users Operations with users. The login logic is also here."

OAuth2PasswordBearer does not work with versioned api

Describe the bug
OAuth2PasswordBearer requires a tokenUrl this does not get api versioned automatically.
How should that be handled?

from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"/auth/login")

Docs URL is served even when set to None

Describe the bug
Even with docs_url set to None, the docs are still served.
Per the FastAPI documentation, the app should no longer serve docs with this option set.

You can disable it by setting docs_url=None.
https://fastapi.tiangolo.com/tutorial/metadata/#docs-urls

def docs_url_kwargs() -> dict:
    return  {
        "openapi_url": None,
        "docs_url": None,
        "redoc_url": None,
    }

application = FastAPI(
    title='Example FastApi',
    description='Nice',
    **docs_url_kwargs(),
)
application = VersionedFastAPI(
    application,
    version_format="{major}",
    prefix_format="/v{major}/api/",
    description='version',
    enable_latest=True,
    **docs_url_kwargs(),
)

To Reproduce
Set docs_url=None when instantiating the FastAPI app and VersionedFastAPI but still see the docs served at /docs.

Expected behavior
Expecting docs to no longer be served (for production use case).

Additional details
Issue may be here:

@parent_app.get(
f"{prefix}/openapi.json", name=semver, tags=["Versions"]
)
@parent_app.get(f"{prefix}/docs", name=semver, tags=["Documentations"])
def noop() -> None:
...

Description from tags metadata not shown in OpenAPI docs

First of all, thank you so much for this extension, saved us a lot of additional unnecessary work!

Bug description
Looks like description from tags metadata is not loaded/visible on OpenAPI docs, even if I pass it directly to VersionedFastAPI constructor. name is loaded, but description not. I haven't checked externalDocs attribute yet.

To Reproduce
Here is the part of the code from my main.py:

tags_metadata = [
    {"name": "products", "description": "Operations with products and product types."},
    {"name": "cameras", "description": "Operations with cameras."}
]

app = FastAPI(title="My App")

app.include_router(product_router.router)
app.include_router(camera_router.router)

@app.get("/")
@version(1, 0)
def root():
    return {"message": "Hello World!"}

app = VersionedFastAPI(app, openapi_tags=tags_metadata)

... and in the product_router.py, I have defined:

router = APIRouter(prefix="/products", tags=["products"])

... and the same way in camera_router.py.

Expected behavior
How it should look like, is described here: FastAPI-Metadata. When I turn off fastapi-versioning, everything works like expected.

Environment

  • Docker version: 19.03.13
  • FastAPI version: 0.62.0 (built with official FastAPI Docker image and manually updated fastapi)
  • fastapi-versioning version: 0.5.0

Thanks!

Add urls with versioning

Is your feature request related to a problem? Please describe.
Without versioning:

app = FastAPI(title=TITLE,
              servers=[
                  {"url": "http://localhost:8081", "description": "Dev environment"},
                  {"url": "https://prod.example.com", "description": "Production environment"},
              ],
              description=DESCRIPTION,
              version=VERSION,
              contact=CONTACTS,
              license_info=LICENSE

              )

Describe the solution you'd like

  • Add an option to use with versioning ,so in the docs will show the servers section with abover servers list.

app decorators not working

Hi,

first of all thank you for this essential extension to FastAPI !
I have a problem that might be to the lack of a functional example only.
I can not get any of the decorators to work together with extending the original app with VersionedFastAPI

@app.on_event("startup")
@repeat_every

the code is structured like in your example, where VersionedFastAPI(app) is the last line after all the endpoints:

app = FastAPI()
... some endpoints...
app = VersionedFastAPI(app)

Thank you very much for your help

Docs showing routes of all versions

Hello,

I have a sample app with two versions of the user endpoint. Version 1 has an endpoint and version 2 has an other endpoint. Now if I navigate to the docs version 1 shows all correct but version two shows both of the endpoints together (the endpoint from v1 and the endpoint from v2)

Here my code:

# app/main.py
from fastapi import FastAPI
from fastapi_versioning import VersionedFastAPI

from app.v1.routers import users as users_v1
from app.v2.routers import users as users_v2

app = FastAPI()

app.include_router(users_v1.router)
app.include_router(users_v2.router)

app = VersionedFastAPI(app, version_format='{major}', prefix_format='/v{major}')
# app/v1/routers/users.py

from fastapi import APIRouter
from fastapi_versioning import versioned_api_route

router = APIRouter(route_class=versioned_api_route(1, 0))


@router.get("/users/v1", tags=["users"])
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]
# app/v2/routers/users.py

from fastapi import APIRouter
from fastapi_versioning import versioned_api_route

router = APIRouter(route_class=versioned_api_route(2, 0))


@router.get("/users/v2", tags=["users"])
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]

Here what I see in the docs when I navigate to http://localhost:8000/v2/docs
image

I would expect the docs of version 2 to only show the endpoint defined in app/v2/routers/users.py and not a combination of both versions.

Thanx for any help with this!

Fix type hint

Hi there! First of all: awesome project, I'm implementing this in my projects soon :D

Now, there's a small typo here in the hint:

def version_to_route(route: APIRoute) -> Tuple[int, APIRoute]:

Should be Tuple[Tuple[int, int], APIRoute]

Can issue a quick PR if needed :)

FastApi StaticFiles with mount not working

Describe the bug
Our team is struggling to get fastapi-versioning working in our environment since we are serving static assets for our frontend and docs. This seems to be an issue with mount when using StaticFiles.

To Reproduce
Steps to reproduce the behavior:

  1. Create the file test.py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi_versioning import VersionedFastAPI, version

app = FastAPI(title="My App")


@app.get("/")
@version(1, 0)
def greet_with_hello():
    return "Hello"


@app.get("/")
@version(1, 1)
def greet_with_hi():
    return "Hi"

app.mount("/", StaticFiles(directory="/"), name="static")


app = VersionedFastAPI(app)
  1. Run the server uvicorn test:app
  2. Error from running application
Traceback (most recent call last):
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/bin/uvicorn", line 8, in <module>
    sys.exit(main())
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/uvicorn/main.py", line 362, in main
    run(**kwargs)
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/uvicorn/main.py", line 386, in run
    server.run()
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/uvicorn/server.py", line 49, in run
    loop.run_until_complete(self.serve(sockets=sockets))
  File "uvloop/loop.pyx", line 1494, in uvloop.loop.Loop.run_until_complete
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/uvicorn/server.py", line 56, in serve
    config.load()
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/uvicorn/config.py", line 308, in load
    self.loaded_app = import_from_string(self.app)
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/uvicorn/importer.py", line 20, in import_from_string
    module = importlib.import_module(module_str)
  File "/usr/local/Cellar/[email protected]/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 790, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "./test.py", line 22, in <module>
    app = VersionedFastAPI(app)
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/fastapi_versioning/versioning.py", line 42, in VersionedFastAPI
    version_routes = [
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/fastapi_versioning/versioning.py", line 43, in <listcomp>
    version_to_route(route, default_version) for route in app.routes
  File "/Users/killswitch-gui/Library/Caches/pypoetry/virtualenvs/portal-pNH9Wcd1-py3.9/lib/python3.9/site-packages/fastapi_versioning/versioning.py", line 24, in version_to_route
    version = getattr(api_route.endpoint, "_api_version", default_version)
AttributeError: 'Mount' object has no attribute 'endpoint'

Expected behavior
A versioned API without having to version mounts.

Additional context
N/A

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.