But if you return a Response directly (or any subclass, like JSONResponse), the data won't be automatically converted (even if you declare a response_model), and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header Content-Type as part of the generated OpenAPI).
But you can also declare the Response that you want to be used (e.g. any Response subclass), in the path operation decorator using the response_class parameter.
The contents that you return from your path operation function will be put inside of that Response.
And if that Response has a JSON media type (application/json), like is the case with the JSONResponse and UJSONResponse, the data you return will be automatically converted (and filtered) with any Pydantic response_model that you declared in the path operation decorator.
Note
If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs.
For example, if you are squeezing performance, you can install and use orjson and set the response to be ORJSONResponse.
Import the Response class (sub-class) you want to use and declare it in the path operation decorator.
For large responses, returning a Response directly is much faster than returning a dictionary.
This is because by default, FastAPI will inspect every item inside and make sure it is serializable as JSON, using the same JSON Compatible Encoder explained in the tutorial. This is what allows you to return arbitrary objects, for example database models.
But if you are certain that the content that you are returning is serializable with JSON, you can pass it directly to the response class and avoid the extra overhead that FastAPI would have by passing your return content through the jsonable_encoder before passing it to the response class.
As seen in Return a Response directly, you can also override the response directly in your path operation, by returning it.
The same example from above, returning an HTMLResponse, could look like:
fromfastapiimportFastAPIfromfastapi.responsesimportHTMLResponseapp=FastAPI()@app.get("/items/")asyncdefread_items():html_content=""" <html> <head> <title>Some HTML in here</title> </head> <body> <h1>Look ma! HTML!</h1> </body> </html> """returnHTMLResponse(content=html_content,status_code=200)
Warning
A Response returned directly by your path operation function won't be documented in OpenAPI (for example, the Content-Type won't be documented) and won't be visible in the automatic interactive docs.
Info
Of course, the actual Content-Type header, status code, etc, will come from the Response object you returned.
If you want to override the response from inside of the function but at the same time document the "media type" in OpenAPI, you can use the response_class parameter AND return a Response object.
The response_class will then be used only to document the OpenAPI path operation, but your Response will be used as is.
fromfastapiimportFastAPIfromfastapi.responsesimportHTMLResponseapp=FastAPI()defgenerate_html_response():html_content=""" <html> <head> <title>Some HTML in here</title> </head> <body> <h1>Look ma! HTML!</h1> </body> </html> """returnHTMLResponse(content=html_content,status_code=200)@app.get("/items/",response_class=HTMLResponse)asyncdefread_items():returngenerate_html_response()
In this example, the function generate_html_response() already generates and returns a Response instead of returning the HTML in a str.
By returning the result of calling generate_html_response(), you are already returning a Response that will override the default FastAPI behavior.
But as you passed the HTMLResponse in the response_class too, FastAPI will know how to document it in OpenAPI and the interactive docs as HTML with text/html:
Keep in mind that you can use Response to return anything else, or even create a custom sub-class.
Technical Details
You could also use from starlette.responses import HTMLResponse.
FastAPI provides the same starlette.responses as fastapi.responses just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
The main Response class, all the other responses inherit from it.
You can return it directly.
It accepts the following parameters:
content - A str or bytes.
status_code - An int HTTP status code.
headers - A dict of strings.
media_type - A str giving the media type. E.g. "text/html".
FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the media_type and appending a charset for text types.
fromfastapiimportFastAPI,Responseapp=FastAPI()@app.get("/legacy/")defget_legacy_data():data="""<?xml version="1.0"?> <shampoo> <Header> Apply shampoo here. </Header> <Body> You'll have to use soap here. </Body> </shampoo> """returnResponse(content=data,media_type="application/xml")
Takes an async generator or a normal generator/iterator and streams the response body.
fromfastapiimportFastAPIfromfastapi.responsesimportStreamingResponseapp=FastAPI()asyncdeffake_video_streamer():foriinrange(10):yieldb"some fake video bytes"@app.get("/")asyncdefmain():returnStreamingResponse(fake_video_streamer())
This is the generator function. It's a "generator function" because it contains yield statements inside.
By using a with block, we make sure that the file-like object is closed after the generator function is done. So, after it finishes sending the response.
This yield from tells the function to iterate over that thing named file_like. And then, for each part iterated, yield that part as coming from this generator function (iterfile).
So, it is a generator function that transfers the "generating" work to something else internally.
By doing it this way, we can put it in a with block, and that way, ensure that the file-like object is closed after finishing.
Tip
Notice that here as we are using standard open() that doesn't support async and await, we declare the path operation with normal def.
You can create your own custom response class, inheriting from Response and using it.
For example, let's say that you want to use orjson, but with some custom settings not used in the included ORJSONResponse class.
Let's say you want it to return indented and formatted JSON, so you want to use the orjson option orjson.OPT_INDENT_2.
You could create a CustomORJSONResponse. The main thing you have to do is create a Response.render(content) method that returns the content as bytes:
fromtypingimportAnyimportorjsonfromfastapiimportFastAPI,Responseapp=FastAPI()classCustomORJSONResponse(Response):media_type="application/json"defrender(self,content:Any)->bytes:assertorjsonisnotNone,"orjson must be installed"returnorjson.dumps(content,option=orjson.OPT_INDENT_2)@app.get("/",response_class=CustomORJSONResponse)asyncdefmain():return{"message":"Hello World"}
Now instead of returning:
{"message":"Hello World"}
...this response will return:
{"message":"Hello World"}
Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉