How to compress json to get it with `bodyParser.json` - node.js

I want to send to my server a compressed json.
I prefer to compress the data in browser.(explainer) and get it to bodyParser.json middleware.
client side function something like this:
export function compressGzip(input) {
const stream = new Response(input).body.pipeThrough(
new CompressionStream('gzip')
)
return new Response(stream)
}
my request in react.js:
return fetch(url, {
headers: {
'Content-Type':'application/json',
'content-encoding': 'gzip'
},
method: 'POST',
body: bodyContent
}).then(res => res.blob())
node.js:
app.use(
bodyParser.json({
limit: "10mb"
})
);
What is the way to do the compression according to this data and does it make sense?
Thank in advance!

I've done this with zlib, but not GZIP.
Firstly, I have this client-side function....
function compressBody(body) {
return new Promise( function( resolve, reject ) {
zlib.deflate(body, (err, buffer) => {
if(err){
console.log("Error Zipping");
reject(err);
}
console.log("Zipped");
resolve(buffer);
});
});
}
To call function....
let compressedBody = await compressBody(JSON.stringify(body));
I can then assign this compressedBody var to the webervice call...
var promise = fetch(ServiceUrl, {method: 'POST', headers:headers, body:compressedBody}).then((result) => result.json() );
You will then need to change the server-side code to accept a compressed body. I used Express and did the following...
var BodyParser = require('body-parser');
require('body-parser-zlib')(BodyParser);
var app = express();
// Other code....
app.use(BodyParser.zlib()); // support json encoded bodies
app.use(BodyParser.json({"inflate": true})); // support json encoded bodies

Got it with blob, it works perfectly :)
The code:
client side:
export function compressGzip(input) {
const stream = new Response(input).body.pipeThrough(
new CompressionStream('gzip')
)
return new Response(stream).blob()
}
const bodyContent = await compressGzip(JSON.stringify({data: data})
return fetch(url, {
headers: {
'Content-Type':'application/json',
'content-encoding': 'gzip'
},
method: 'POST',
body: bodyContent
}).then(res => res.blob())
server side - node.js:
app.use(
bodyParser.json({
limit: "10mb"
})
);

Related

How to hide my API Key in a POST request?

I want to hide my API key when I am making a post request from my browser. I need to input a number plate and send the request to the API and the API responds me with details about it. I have managed to get this working with a GET request to another API by using nodeJS but I can't manage to make it work with a post request. Keep in mind the request needs information from my input field in the browser which is the registration number of the vehicle to send me information back about it.
Here is my function in my browser JS file.
const searchBtn = document.getElementById("search-btn")
function startFetch() {
let plate = document.getElementById("plate").value
let reg = {registrationNumber: `${plate}`}
fetch(`https://driver-vehicle-licensing.api.gov.uk/vehicle-enquiry/v1/vehicles`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': `my key is here`,
},
body: JSON.stringify(reg),
})
.then(response => response.json())
.then(response => {
console.log(response);
})
.catch(err => {
console.log(err);
});
};
searchBtn.addEventListener("click", startFetch);
Any help and method would be appreciated. Thanks in advance.
For anyone in the same boat. I have managed to achieve what I want.
Client side JS file:
function startFetch() {
let plate = document.getElementById("plate").value
let reg = {registrationNumber: plate}
fetch(`http://localhost:3000/v`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(reg),
})
.then(response => response.json())
.then(response => {
console.log(response);
})
.catch(err => {
console.log(err);
});
};
And the backend using Express, NodeJS, body-parser and axios
require("dotenv").config()
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const axios = require('axios');
app.use(bodyParser.json());
app.use(express.static("src"))
//Env vars
const API_URL = process.env.API_URL
const API_KEY = process.env.API_KEY
app.post('/v', (req, res) => {
const body = req.body;
// Make a request to the backend API
axios.post(API_URL, body,
{
headers: {
"Content-Type": "application/json",
'x-api-key': API_KEY
}
}
)
.then((response) => {
// Return the response from the backend API to the client
res.send(response.data);
})
.catch((error) => {
// Handle any errors
res.status(500).send(error);
});
});
app.listen(3000, () => {
console.log('API proxy server is listening on port 3000');
});
You are already sending the body.
A very minor modification to you code:
function startFetch() {
let plate = "abc123";
let reg = { registrationNumber: `${plate}` };
fetch(`https://driver-vehicle-licensing.api.gov.uk/vehicle-enquiry/v1/vehicles`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": `my key is here`,
},
body: JSON.stringify(reg),
}
);
}
startFetch();
You can see your api-key in the header (though you should never send secret via http):
Then in the body (in chrome they call it payload):

Cannot send form data as 'Multipart/formdata'

Cannot send form data as 'Multipart/formdata' Content type in react-native expo app.
when we send formData object in post request in react -native app, we cant get req.body of req.files from node backend
export const saveUserAddVisitor = async data => {
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await axios.post(
`${apiUrl}/api/v1/mobile/apartment/member`,
data,
{
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
},
);
return addVisitorData;
} catch (err) {
return err;
}
};
You can try something like this which works without Axios:
export const saveUserAddVisitor = async data => {
var data = new FormData()
data.append('foo', {
...
})
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await fetch(`${apiUrl}/api/v1/mobile/apartment/member`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data'
},
body: data
})
return addVisitorData;
} catch {
return err;
}
}
After I gave you an example of working code for the client, but the problem may be from the server ;)
Your code with axios look like fine, just be sure to send FormData type, like RĂ©mi said here https://stackoverflow.com/a/72454168/16205278
You can construct your FormData before this function and use it directly in your current code with your axios function.
Service :
import axios from "axios";
import configConstants from "./config.js";
/**
* Current function to save User Add Visitor
* #param {*} data
*/
export const saveUserAddVisitor = async (data) => {
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await axios.post(
`${apiUrl}/api/v1/mobile/apartment/member`,
data,
{
headers: {
Accept: 'application/json',
"Content-Type": "multipart/form-data"
}
}
);
return addVisitorData;
} catch (err) {
return err;
}
};
Use :
import {saveUserAddVisitor} from "./index"
const form = new FormData();
form.append("visitor", { firstName: "Jack", lastName: "Doe" });
saveUserAddVisitor(form);
API Express :
Apparently, express can't resolve multipart-form data unless some help, according following ressource : https://codex.so/handling-any-post-data-in-express
You have to use multer middleware :
const multer = require('multer');
app.post('/', multer().none(), function (req, res, next) {
req.body;
//... some code
});

how to perform operations with json string from axios?

Background
I am passing variable from my frontend HTML file using axios
var idToken1 = result.getIdToken();
axios({
method: 'post',
url: '/trial',
data: idToken1,
headers: {'Content-Type': 'application/x-www-form-urlencoded' }
})
.then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
in my app.js under route getting this as output,here all the values are present in key and key values is empty. so i think i need a way to parse those keys first
{
'{"payload":{"cognito:username":"jatin","exp":1620965984,"iat":1620962384,"email":"xxxx#gmail.com"}}': ''
}
i want to extract "email":"xxxx#gmail.com"
update: in app.js i am already using express native parser app.use(express.json()); app.use(express.urlencoded({ extended: true }));`
A parser is required if you want to get the http body as object:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));
Then you can use req.body to get the http body sent by your html web:
app.post('/trial', function(req, res) {
var email = req.body.payload.email;
//etc
});
The problem was with axios. axios needed object to be send as json object.
var idToken1 = result.getIdToken();
the following code results in proper JSON object in my backend
axios({
method: 'post',
url: '/trial',
data: { idToken: idToken1 },
headers: { 'Content-Type': 'application/json' }
})
.then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
The Problem cause is 'Content-Type'
is should be 'application/json'
axios({
method: 'post',
url: '/trial',
data: idToken1,
headers: { 'Content-Type': 'application/json' }
})
.then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});

Uploading blob/file in react-native, contents is empty

I am able to succesfully upload a blob with proper contents from my web browser, but when I do it from react-native, the upload file is empty. Here is the code:
async function doit() {
const data = new FormData();
data.append('str', 'strvalue');
data.append(
'f',
new File(['foo'], 'foo.txt', {type: 'text/plain'}),
);
await fetch('http://localhost:3002/upload', {
method: 'POST',
body: data
});
}
However doing this same code from react-native, it uploads, but the file is empty.
Here is the node.js server I am using to test this. Loading http://localhost:3002 gives you a button called "upload it". Clicking it does the upload from the web. Screenshots of results are below.
var multiparty = require('multiparty');
var http = require('http');
http
.createServer(function (req, res) {
if (req.url === '/upload' && req.method === 'POST') {
console.log('multipart here');
var form = new multiparty.Form();
form.parse(req, function (err, fields, files) {
console.log(require('util').inspect({ fields, files }, false, null, true));
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ bar: true }));
});
return;
}
console.log('here');
// show a file upload form
res.writeHead(200, { 'content-type': 'text/html' });
res.end(
`
<script>
async function doit() {
const data = new FormData();
data.append('str', 'strvalue');
data.append(
'f',
// new File([new Blob(['asdf'], {type : 'text/plain'})], 'filename.txt'),
new File(['foo', 'what', 'the', 'hell'], 'foo.txt', {type: 'text/plain'}),
);
const res = await fetch('http://localhost:3002/upload', {
method: 'POST',
body: data
});
console.log(JSON.stringify(res, null, 4));
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('b').addEventListener('click', doit, false)
}, false);
</script>
<button type="button" id="b">upload it</button>
`
);
})
.listen(3002);
From web browser we see the node server logs this, notice file size is 14.
However from react-native we see file size is 0:
I faced the same problem recently while posting an image from a react-native app to a server. However, I was able to make it work by appending the name and type of the file to the formData instance.
Here, the uri argument to uploadImageAsync is passed as a route parameter from the previous screen.
const postShoutHandler = async () => {
setShoutUploadStatus("Started Upload");
const response = await uploadImageAsync(route.params.captures);
const uploadResult = await response.json();
if (uploadResult === "Upload successful") {
setShoutUploadStatus("Success");
navigation.navigate("Home");
} else {
setShoutUploadStatus("Failed");
}
};
/* <--Upload image function --> */
const uploadImageAsync = (uri: string) => {
const apiUrl = "https://www.yourserver.com/image";
let uriParts = uri.split(".");
let fileType = uriParts[uriParts.length - 1];
let formData = new FormData();
formData.append("img", {
uri,
name: `photo.${fileType}`,
type: `image/${fileType}`,
});
formData.append("description", "HEY");
let options = {
method: "POST",
body: formData,
headers: {
Accept: "application/json",
"Content-Type": "multipart/form-data",
Authorization: "Bearer " + accessToken,
},
};
return fetch(apiUrl, options);
};
/* <--Upload image function --> */
Here is the Image configuration.
const photoData = await camera.takePictureAsync({
base64: true,
exif: false,
});

Write After End Error when Reading a File using FS

I built a program where a user can send request with a PDF URL then sever download it and forward into an external API endpoint. Now, the code able to download file but it hit this error when it start to read the file.
I must admit that Promises is something I hate to learn hence I used Async Function with Awaits and in other cases I used normal functions. Promises is so hard to grasp. The syntax make it hard to read.
Code is below:
const fs = require('fs');
const url = require("url");
const path = require("path");
const http = require('http')
const rp = require('request-promise');
const app = express();
const port = 8999;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/upload-invoice', (req, res) => {
var parsed = url.parse(req.body.FileURL);
var filename = (path.basename(parsed.pathname));
var downloaded_file_path = `invoices/${filename}`;
function download() {
var options = {
hostname: parsed.hostname,
port: 2799,
path: parsed.path,
method: 'GET'
}
const file = fs.createWriteStream(`invoices/${filename}`);
const make_request = http.request(options, (response) => {
response.pipe(file);
});
make_request.end();
try {
setTimeout(function () {
upload()
}, 1000);
} catch (error) {
console.log('An Error occured when uploading file,'+error);
}
}
async function upload() {
const flow = "Upload Invoice"
var file_stream = fs.createReadStream(downloaded_file_path)
var options = {
method: 'POST',
strictSSL: true,
uri: 'https://endpoint.goes.here',
formData: {
'file': file_stream
},
headers: {
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryzte28ISYHOkrmyQT'
},
json: req.body,
resolveWithFullResponse: true
}
try {
var response = await rp(options)
res.send(response.body)
}
catch (error) {
console.log(`An error on ${flow} flow for unknown user. Here is more info about error,
${error}
`)
res.send("Error")
}
}
download()
});
app.listen(port)
Update:
formData: {
name: filename,
file: {
value: fs.createReadStream(downloaded_file_path),
options: {
filename: filename,
contentType: 'application/pdf'
}
}
}
I've tried this code too but it output same error.
It works after removing json: req.body
My fault.

Resources