I wouldn't suggest using PyFPDF
, as it is outdated and no longer being maintained. Instead, you could use fpdf2
(see the documentation as well), which is a fork and the successor of PyFPDF
, as well as has very similar syntax. You could install it as follows:
pip install fpdf2
As described in this tutorial, when calling FPDF.output()
without providing any filepath parameter, the function returns the PDF bytearray
buffer, which you could convert to a bytes
object using the bytes()
function and pass it to a custom Response
(if you need to return it as part of a Jinja2 template, see here). As described in this answer, to have the PDF file viewed in the browser, you could set the Content-Disposition
response header as follows:
headers = {'Content-Disposition': 'inline; filename="out.pdf"'}
Alternatively, to have the PDF file downloaded rather than viewed in the borwser, use:
headers = {'Content-Disposition': 'attachment; filename="out.pdf"'}
Here are two options on how to generate and return a PDF file from a FastAPI endpoint. The first option uses an endpoint defined with normal def
, while the second option uses an async def
endpoint. Please have a look at this answer to understand the difference between the two, as well as how FastAPI handles requests for def
endpoints compared to async def
endpoints.
Option 1 - Using def
endpoint
from fastapi import FastAPI, Response
from fpdf import FPDF
app = FastAPI()
def create_PDF(text):
pdf = FPDF()
pdf.add_page()
pdf.set_font('helvetica', 'B', 16)
pdf.cell(10, 30, text)
return pdf.output()
@app.get('/')
def get_pdf():
out = create_PDF('Hello World!')
headers = {'Content-Disposition': 'inline; filename="out.pdf"'}
return Response(bytes(out), headers=headers, media_type='application/pdf')
Option 2 - Using async def
endpoint
from fastapi import FastAPI, Response
from fpdf import FPDF
import asyncio
import concurrent.futures
app = FastAPI()
def create_PDF(text):
pdf = FPDF()
pdf.add_page()
pdf.set_font('helvetica', 'B', 16)
pdf.cell(10, 30, text)
return pdf.output()
@app.get('/')
async def get_pdf():
loop = asyncio.get_running_loop()
with concurrent.futures.ThreadPoolExecutor() as pool:
out = await loop.run_in_executor(pool, create_PDF, 'Hello World!')
headers = {'Content-Disposition': 'inline; filename="out.pdf"'}
return Response(bytes(out), headers=headers, media_type='application/pdf')