How to return a pdf file from a rest api?

Andrew_tainton picture Andrew_tainton · Mar 15, 2017 · Viewed 13.4k times · Source

I have setup a rest API inside a ruby on rails application, I now have a requirement to generate a PDF and return this PDF from a get request. I am looking for some advice on how to implement this feature.

Some of the requirements that I have are as follows: I can't save the file and give the end user a link to the file because the data in the file can be updated at any time. I am using the application as microservice so there isn't a front end that I can use to display the file.

So here is my thinking I would love some advice on how to implement this feature.

I would like to make a get request to a specific endpoint in the application. I expect a PDF file to be returned which I can then display to the end user.

I am currently using WickedPdf gem to generate a temporary PDF file, but I am really struggling with how the response should look.

Any advice would be much appreciated.

Answer

Peter Toth - Toma picture Peter Toth - Toma · Mar 15, 2017

One way is to create a PDF file in memory and stream it to the client. I prefer this way, maybe later you will have to send PDF files via email, or just save them to some backup disk etc...

def get_pdf
  pdf = WickedPdf.new.pdf_from_string('<h1>Hello There!</h1>')
  send_data pdf, filename: 'file_name.pdf'
end

You can put the PDF generation to a different service and just call it in the controller. This provides isolation and you can test it separately.

Also you can debug the endpoint response with HTTPie http get http://localhost:3000/invoices/1/get_pdf

Rails will set all the necessary HTTP response headers:

Content-Disposition: attachment; filename="file_name.pdf"
Content-Length: 5995
Content-Transfer-Encoding: binary
Content-Type: application/pdf

So when the user clicks on a link that points to the endpoint, most probably the download dialog will pop up because of the Content-Disposition: attachment; header

Other solution is to render the get_pdf.html as PDF and send back to the client:

def get_pdf
  render pdf: "file_name"
end

But in this case the Content-Disposition header will be inline, which means the browser will open the pdf (if it can read PDF format) instead of offering to download it.