Search code examples
pythonpython-requestsfastapihttp-status-codesstarlette

Why is the client receiving a different status_code than the sent one?


I have this endpoint in my API which calls another endpoint in another API. I'm just returning the received response to the client, but sometimes the client is actually receiving a different status_code than the one I'm passing in the return statement.

I'm doing it something like this (using FastAPI):

@router.post("/my_endpoint")
async def my_method():
    ...
    response = requests.post(url=<url_to_the_other_api>, headers=headers, timeout=10)
    return response.json()

The client is sometimes receiving an error message but still with status_code 200. I thought I was returning the same received response object, but apparently not.

May someone please explain it?

I thought json() method or FastAPI could be creating a new response object and just using the old body. Is that what happens? This behavior is quite unclear to me.


Solution

  • When using return response.json() you are just returning the content of the response that you are getting from the request issued using Python requests to <the_other_api>. FastAPI, behind the scenes, will then put the JSON data into a JSONResponse and reuturn it back to the client using a status_code value of 200 (see this answer for more details).

    If you would like to use the status_code (as well as the media_type/content-type) returned in the response from <the_other_api>, you could then return a custom Response directly.

    Example

    from fastapi import FastAPI, Response
    import requests
    
    app = FastAPI()
    
    @app.get('/')
    def root():
        # returns 400 Error Not Found Response (for testing purposes)
        #r = requests.get(url='http://httpbin.org/NotValidRoute')
        
        # reurns 200 Successful Response
        r = requests.get(url='http://httpbin.org/json')
        
        return Response(content=r.content, media_type=r.headers['content-type'], status_code=r.status_code)
    

    I would, however, strongly recommend using the httpx library instead of requests (httpx has very similar syntax to requests, is easy to use and provides async support), as demonstrated in this answer, as well as here and here. If you would rather keep using requests, please make sure to define the endpoint with normal def (as shown in the exmaple above) not async def (as shown in your question)—have a look at this answer for more details as to why.