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')