How to serve a PDF in Express/Node and download in client button? - node.js

I have an Express/Node server with an endpoint to provide the PDF, and a React client that is making a get request to that endpoint.
Currently the PDF is in what I believe is raw pdf binary data string form (obtained from the response of a separate api request), and on the client side I want to click a button in the browser and have the PDF download.
I'm not sure what is needed on the server side and on the client side to successfully trigger
the download on request.
Do I need to convert the binary PDF data to something on the server side first? Or does this get handled on the client side?
And what would be the code to do this?
Server:
const servePDF = async (req, res) => {
const pdfBinary = '%PDF-1.4 %âãÏÓ 2 0 obj <</ColorSpa ......'
~code here to send pdf as response~
}
Client:
const downloadPDF = async () => {
~get request code here, to run onClick of a button, to download file~
}

Related

Fastify - Sending a PDF from node.js server

I have an API built on node.js with Fastify.
The server generates a PDF which I'm then trying to send to a client via an API request. However the request I see in Chrome under network shows the type as XHR, despite me having set the content type like so:
const fs = require('fs')
const stream = fs.createReadStream('../test.pdf', 'binary')
reply.header('Content-Type', 'application/pdf')
reply.send(stream).type('application/pdf').code(200)
On the client side, I'm just making a simple POST request and assuming if its a PDF, the browser will just download it.
axios.post('http://127.0.0.1:8080/contract', requestBody)
.then((response: any) => {
setIsSubmitting(false)
})
.catch((error: any) => {
//TODO
});
(Also, the other thought I had was is this actually the correct way to send i.e. with fs.createReadStream - it's just a static file).
Any ideas what I'm doing wrong in the above.
I had the same problem. Found out this is a problem with fastify, you can upgrade to the latest (Today is 3.27.2).
The issue: https://github.com/fastify/fastify/issues/3283
Bug fix: https://github.com/fastify/fastify/pull/3285

Post a simple react form - node, axios, nodemailer, backend, postman

I've set up a react form running on http://localhost:3000/about and I've the backend running on port 5000 (localhost:5000). On react's package.json I set up "proxy":"localhost:5000:.
When I use postman and I send the post to localhost:5000/api/contact, the email is sent correctly (I send the data as JSON - name, email and message). Status 200
When I use the react form, the data is well prepared as json but I can't figure out the baseURL to send correctly the method post. status 404. I tried:
localhost:3000/about/api/contact;
localhost:3000/api/contact;
localhost:3000/api.... None works...
FYI
the server is set up with the following middleware and is working ok:
app.use('/api',contactRoute)
the controller is imported and coded as following:
router.post('/contact', (req, res)=>{
const data = req.body;
The React part is not posting correctly with axios and is coded as following:
onSubmit: async (values) => {
//values.preventDefault();
try {
const data = (JSON.stringify(values, null, 2))
setLoader(true);
console.log(data)
await axios.post('/contact', data);
The method post in react is never completed, when I check the console.log of data, is correctly structured as JSON...status 404
use the post parameter in your axois request {port: 5000} then It will use your actual backend port.
By default, axios uses the base path from the URL. So, here when an API request is made.
await axios.post('/contact', data);
It is actually making the post request on localhost:3000 rather than your backend server at localhost:5000. Also, "api" should also be prepended.
One simple way is to use absolute URL which should work.
await axios.post('http://localhost:5000/api/contact', data);

running function after res.send

I'm trying to run this code
module.exports = async (req, res, next) => {
res.set('Content-Type', 'text/javascript');
const response = {};
res.status(200).render('/default.js', { response });
await fn(response);
};
fn is a function that calls an api to a service that will output to the client something. but its dependent on the default.js file to be loaded first. How can do something like
res.render('/default.js', { response }).then(async() => {
await fn(response);
};
tried it, but doesn't seem to like the then()
also, fn doesn't return data to the client, it calls an api service that is connected with the web sockets opened by the code from default.js that is rendered.
do i have to make an ajax request for the fn call and not call it internally?
any ideas?
Once you call res.render(), you can send no more data to the client, the http response has been sent and the http connection is done - you can't send any more to it. So, it does you no good to try to add something more to the response after you call res.render().
It sounds like you're trying to put some data INTO the script that you send to the browser. Your choices for that are to either:
Get the data you need to with let data = await fn() before you call res.render() and then pass that to res.render() so your template engine can put that data into the script file that you send the server (before you send it).
You will need to change the script file template to be able to do this so it has appropriate directives to insert data into the script file and you will have to be very careful to format the data as Javascript data structures.
Have a script in the page make an ajax call to get the desired data and then do your task in client-side Javascript after the page is already up and running.
It looks like it might be helpful for you to understand the exact sequence of things between browser and server.
Browser is displaying some web page.
User clicks on a link to a new web page.
Browser requests new web page from the server for a particular URL.
Server delivers HTML page for that URL.
Browser parses that HTML page and discovers some other resources required to render the page (script files, CSS files, images, fonts, etc...)
Browser requests each of those other resources from the server
Server gets a request for each separate resource and returns each one of them to the browser.
Browser incorporates those resources into the HTML page it previously downloaded and parsed.
Any client side scripts it retrieved for that page are then run.
So, the code you show appears to be a route for one of script files (in step 5 above). This is where it fits into the overall scheme of loading a page. Once you've returned the script file to the client with res.render(), it has been sent and that request is done. The browser isn't connected to your server anymore for that resource so you can't send anything else on that same request.

How to load changes instantly - React Native

Question
How do I load new data every time it is added to the server?
What I would like to have happen
User opens app
User uploads text to server
The app displays the text from the server immediately
What is currently happening
Users uploads text to server
User reloads app
The app displays the text from the server
Server Side
I am using expressJS for the server and then I am saving all of the text that the server gets to an off-site mongoose DB.
More info
I am using a Node JS server
I am using the fetch API to communicate between the server and the
client
The client app is written with React Native
I don't know how your app makes requests. Some code from both server and client would be useful. But i can think two possible solutions for your situation:
Return your text from upload request
Since you need server's response AFTER user uploads text to server, you can use response data within upload request. Here is an example code:
fetch('https://your-server.com/api/upload-text/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
text: 'yourValue',
})
}).then(response => response.json())
.then(responseJson => {
// Here is your text returned from server
let serverText = responseJson.text
// Set your state here with new data
this.setState({
textFromServer: serverText
});
// Now you can display new data in your render method.
})
With this way, users do not need to reload the app to fetch new text data. Your app will be updated after server responds to upload request.
WebSocket
If the data that you need from server is not depends on a client request, you can use websockets. With this way, you can fetch / send any data from server or from other clients any time. React Native supports websockets. More on here

angularjs nodejs download file from apache http server

I want to download a pdf file when clicking a button on a html page.The html page is deployed on nodejs.And the pdf file is on apache http server.So the html page and nodejs server are on the same domain, but the html page and apache http server are cross domain.
Can I do like this?
when I click the button, I send a request to nodejs, and nodejs get file from apache http server, and then send response to client(browser).Can this case work?
And if the pdf file is very large, I can wait until nodejs get it over and then send response to client.I just want to achieve this goal, download this file like <a href="xxx" download="yyy">Download<a>
Can I achieve this goal?
Yes, you can do that. Browser -> Node.JS Server -> Http Server -> Node.JS Server -> Browser Using Request module you can stream files between req and res. https://www.npmjs.com/package/request#streaming
Example:
http.createServer(function (req, resp) {
if (req.url === '/doodle.png') {
r.get('http://google.com/doodle.png').pipe(resp)
}
}).listen(port);

Resources