I am new to the exporting CSV from JSON... I am pretty close to gettin this... but I dont know how to send the CSV back to the client from my express server.
The user selects the customers to export, and hits the export button... I'd like the CSV file to be sent back.
I got the JSON data converted to CSV on the server, how would I send this file back to the client?
Here is my express route so far -
router.post('/SaveCSV', (req, res) => {
const csv = JSONToCSV(req.body, { fields: ["Customer Name", "Business Name", "Customer Email", "Customer ID", "School ID", "Student Count", "Admin Count", "Courses Count" ]})
})
I have tried res.send, res.sendFile, and res.attachment.. any guidance?
Update***
router.post('/SaveCSV', (req, res) => {
const csv = JSONToCSV(req.body, { fields: ["Customer Name", "Business Name", "Customer Email", "Customer ID", "School ID", "Student Count", "Admin Count", "Courses Count" ]})
res.attachment('CustomerData.csv').send(csv)
})
Here is my fetch call on the client --
const saveCsv = async (customers) => {
const token = await getAccessTokenSilently();
try {
const response = await fetch('/api/SaveCSV', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
"content-type": 'application/json; Charset=UTF-8'
},
body: JSON.stringify(customers)
})
const responseData = await response.json()
console.log(responseData)
} catch (error) {
console.log(error)
}
}
I am new to the exporting CSV from JSON... I am pretty close to gettin this... but I dont know how to send the CSV back to the client from my express server.
The user selects the customers to export, and hits the export button... I'd like the CSV file to be sent back.
I got the JSON data converted to CSV on the server, how would I send this file back to the client?
Here is my express route so far -
router.post('/SaveCSV', (req, res) => {
const csv = JSONToCSV(req.body, { fields: ["Customer Name", "Business Name", "Customer Email", "Customer ID", "School ID", "Student Count", "Admin Count", "Courses Count" ]})
})
I have tried res.send, res.sendFile, and res.attachment.. any guidance?
Update***
router.post('/SaveCSV', (req, res) => {
const csv = JSONToCSV(req.body, { fields: ["Customer Name", "Business Name", "Customer Email", "Customer ID", "School ID", "Student Count", "Admin Count", "Courses Count" ]})
res.attachment('CustomerData.csv').send(csv)
})
Here is my fetch call on the client --
const saveCsv = async (customers) => {
const token = await getAccessTokenSilently();
try {
const response = await fetch('/api/SaveCSV', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
"content-type": 'application/json; Charset=UTF-8'
},
body: JSON.stringify(customers)
})
const responseData = await response.json()
console.log(responseData)
} catch (error) {
console.log(error)
}
}
Share
Improve this question
edited Jul 31, 2020 at 21:22
Nick McLean
asked Jul 31, 2020 at 21:12
Nick McLeanNick McLean
6411 gold badge9 silver badges26 bronze badges
5
- You are right - res.json(csv) sends back the data just fine, but in a string. res.send(csv) and the attachment gives me "SyntaxError: Unexpected token , in JSON at position 15" – Nick McLean Commented Jul 31, 2020 at 21:19
- I added the fetch call from the client as well as my route from the server above! Thanks for your help so far. Maybe its because I am awaiting response.json()? – Nick McLean Commented Jul 31, 2020 at 21:24
- Dang - I have never requested anything other than json back! hmm. Would you be able to point me in the right direction? Would maybe be awaiting res.attachment? – Nick McLean Commented Jul 31, 2020 at 21:29
- I'd love the user to download the file. Maybe arraybuffer? I have seen that before when dealing with uploading files – Nick McLean Commented Jul 31, 2020 at 21:31
- Ahh so convert the CSV client side.... Idk why I went this way now either.. I guess I was just going down the hole of what I read how to do. I'll fix this! – Nick McLean Commented Jul 31, 2020 at 21:56
2 Answers
Reset to default 10On the server side, you can just send
the CSV data back. By default that will e with content type text/html
, though - better to send it with the correct type text/csv
. This could be done by using res.type('text/csv')
first.
Now, it is debatable if this is necessary if you don't navigate the browser to your endpoint anyway but consume the download programmatically in your frontend code, but: To mark it as a "download" (that should not be displayed but actually saved to a file), you can use res.attachment()
. If you specify a filename, it will then also send that filename in the header but also set the content type automatically based on the extension. So you can use res.attachment('customers.csv').send(csv)
for example. If a browser were to navigate to your URL, it would see the CSV as a file download with name customers.csv
.
router.post('/SaveCSV', (req, res) => {
const csv = JSONToCSV(req.body, { fields: ["Customer Name", "Business Name", "Customer Email", "Customer ID", "School ID", "Student Count", "Admin Count", "Courses Count" ]})
res.attachment('customers.csv').send(csv)
})
On the client side, you could then get the raw response body as a blob using response.blob()
, and you could have the user download that blob. For more explanation how that could be done, you can research online (there are lots of resources about that). A full example that gets the filename right automatically and works with legacy browsers as well would be here (which was also linked in a ment to this Stack Overflow answer) - note that you'll need text/csv
instead of application/pdf
or read the content type from the response.
Let me instead post a simplified example with less functionality:
const blob = await response.blob()
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = 'customers.csv'
link.click()
setTimeout(() => URL.revokeObjectURL(link.href), 0)
But let's think this over. According to the code you showed, all your server code does is take the data from the frontend, stuff it into a CSV and send it back. I would say, you don't need the server for that at all! You could do the whole thing in the frontend code to begin with!
const saveCsv = (customers) => {
const csv = JSONToCSV(customers, { fields: ["Customer Name", "Business Name", "Customer Email", "Customer ID", "School ID", "Student Count", "Admin Count", "Courses Count" ]})
const link = document.createElement('a')
link.href = 'data:text/csv,' + encodeURIComponent(csv)
link.download = 'customers.csv'
link.click()
}
This assumes of course that you can make the JSONToCSV
library available in the frontend, or that you would add your own function to build the CSV data.
The JSONtoCSV
probably generates a string in the format of CSV from JSON.
From this string, you have two options.
send the string into a text document, then
res.sendFile
that file.This can be done as such.
I remend using this package, although you can do it vanilla. Just create the file and write the string to it, then send it via
res.sendFile
.const csv = new ObjectsToCsv(csvdata); await csv.toDisk("./data.csv"); res.sendFile("./data.csv");
send the string as JSON, then parse it into a download in the frontend. This is the most mon approach.
This can be done as shown here:
// Parse it into CSV with a frontend CSV library. // Then, create a hidden element and automatically download the file. var hiddenElement = document.createElement('a'); hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csv); hiddenElement.target = '_blank'; hiddenElement.download = 'exported.csv'; hiddenElement.click();
Source