Make CRUD request with Nodejs - node.js

I want to make ajax calls with nodeJS to an endpoint. I have done many "get" requests by doing
http.get('url', (res) => ....)
Now , I want to make "post, put and delete" request
For example, I would to like to do
http.post('url', 'body', (res) => ...)
http.put....
http.delete ...
When I did that http.post request, I obtain "TypeError: "listener" argument must be a function".
And this is my complete request
http.post(environment.url , req.body, (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
return res.status(200).send(JSON.parse(data))
});
}).on("error", (err) => {
res.status(404).send(err)
});
It is possible ? Many thanks

For some reason NodeJS don't seem to implement the other HTTP methods as their own methods in the http module. You'll have to use http.request and set the request method in the option.
In its most simple form, you should be able to write
var req = http.request({method: 'POST', path: url}, (res) => ...);
req.write(body);
req.end();
Source:
https://nodejs.org/api/http.html#http_http_request_options_callback

Related

Nodejs, express http request - response not unzipping

I am trying to write a function that creates an HTTPS request.
This is all part of an expressjs project with Typescript.
I can get the HTTPS request working and getting a response - but the response is encoded with GZIP. I am trying my best to follow the documentation. But no luck, the response stays zipped.
This is my code
private getData = (host, pathname): Promise<string> => {
return new Promise((resolve, reject) => {
const options = {
hostname: host,
path: pathname,
gzip: true,
method: 'GET',
headers: {'x-apikey': 'XXXX'}
}
const req = https.request(options, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
console.log('error!')
return reject(new Error('statusCode=' + res.statusCode));
}
let body = '';
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
try {
console.log('res.headers', res.headers)
console.log('res.headers', body)
body = JSON.parse.toString();
resolve(body);
} catch (e) {
reject(e);
}
resolve(body);
});
req.on('error', (e) => {
reject(e.message);
});
// send the request
});
req.end();
});
}
The console log statement shows this
So obviously the JSONparsing is failing. What am I missing?
Thanks heaps for the help
https.request() does not natively support gzip compression. So, you will have to either add support for it yourself as shown here or use an https request library such as got() that already supports gzip (and also already supports promises too).
import got from 'got';
private getData = (host, pathname): Promise<string> => {
const options = {
method: 'GET',
headers: {'x-apikey': 'XXXX'}
}
return got(`https://${host}${pathname}`, options).json();
}
FYI, it's not clear in your question what type of response you are expecting. You attempt to use:
body = JSON.parse.toString();
which doesn't make any sense because JSON.parse.toString() is trying to get string version of the JSON.parse function without actually even calling that function - very odd. Perhaps you meant JSON.parse(body)? But, your typescript makes it look like you're expecting a promise that resolves to a string so that wouldn't usually be the result of JSON parsing.
If you are expecting a gzipped JSON response, then you would use the:
return got(`https://${host}${pathname}`, options).json();
I show above. If you are expecting just a string, not JSON, then you would change that line to:
return got(`https://${host}${pathname}`, options).text();

Express JS - Network connection was lost

I am trying to set up an express js server that will be hosting a mongodb database. Everything is pretty standard: I have some routes open that will take in data from the client and then store that in the database.
Here is my query string:
let url = "http://xxx.xxx.xx.xxx:3000/update/data=" + JSON.stringify(params);
What I have noticed is that if params doesn't contain much information, it works fine. However, if params is contains a lot of information, then the client throws this error:
Failed to load resource: The network connection was lost.
Http failure response for (unknown url): 0 Unknown Error
(This same error is happening in both Safari and Chrome.)
For example, if params is as below:
{
"accountId": "12345678910",
"data": [
1, 2, 3, 4
]
}
then there is no issue. However, if params.data is a huge array with a ton of information in it instead of just [1, 2, 3, 4], then the error is thrown.
Also, my express server never even seems to receive the request. No logs; nothing. What I would expect to happen is just a normal response and result, however it seems like the client is just giving up on sending something large. Perhaps it has something to do with sending it as a big string?
You put your data on your URL. But, URLs have limited length.
You need to use POST and put your data in the HTTP request body.
You haven't shown us how you use that URL, so it's hard to make suggestions about altering your code. Using the http request operation is the way to go. Something like this might work...
const payload = JSON.stringify(params);
const url = 'http://xxx.xxx.xx.xxx:3000/update/';
const options = {
method: 'POST', // <--- tell it to POST
headers: {
'Content-Type': 'application/json', // <--- tell it you're posting JSON
'Content-Length': payload.length; // <--- tell it how much data you're posting.
}
};
const req = http.request(url, options, (res) => {
/* handle stuff coming back from request here */
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
let chunks=[];
res.on('data', (chunk) => {
chunks.push(chunk);
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
const resultingData = chunks.join();
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
// write data to request body
req.write(payload);
req.end();

ERR_HTTP_HEADERS_SENT Error Generated When Redirecting Requests

I am using the Node.js framework and Express module to write an API wrapper that redirects requests to another server. I can successfully redirect the request to the target server and I receive a valid response containing a JSON payload. However, after the initial request, if I try another request I get the following error.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
This is a sample of the code I wrote for the HTTP GET Express route:
app.get('/companyrecords/:name', function(req, res) {
const options = {
protocol: 'http:',
hostname: 'myhost',
port: 5001,
method: 'GET',
path: '/path/to/resource/?name=name',
auth: 'username:password',
headers: {
Connection: 'close'
}
}
const myAppReq = http.request(options, (myAppRes) =>{
console.log(`STATUS: ${myAppRes.statusCode}`);
myAppRes.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
res.send(chunk);
});
myAppRes.on('end', () => {
res.end('No more data to send.');
});
});
myAppReq.on('error', (err) => {
console.error(`Problem with request: ${err.message}`);
});
myAppReq.write('');
myAppReq.end();
});
Not sure why I am getting this error since I am calling the req.write() method so that the request's headers are sent. When looking at the error stack trace it appears the error occurs when I call the res.send() method inside the callback to the 'data' event. Perhaps I'm not understanding the flow of execution among the request or the sequence in which the events are being emitted. Any guidance/information would be greatly appreciated.
You shouldn't be sending the response inside data event callback because response will be sent when you will receive the first chunk of data. What you should do is to write the chunk to response stream and send response inside end event callback:
const myAppReq = http.request(options, (myAppRes) =>{
myAppRes.on('data', (chunk) => {
res.write(chunk);
});
myAppRes.on('end', () => {
res.end();
});
});

Unable to send request from request callback

I cant execute a second request inside the request callback.
request({
url: url,
headers: {
'auth-token': token
},
method: 'POST',
json: true,
body: data
}, (err, req, body) => {
if (err) {
console.log(err);
} else {
// Prosses data;
// This is the second request.
request({
url: url2,
headers; {
'auth-token': token
},
method: 'POST',
json: true,
body: data2
}, (err, req, body) => {
if (err) {
console.log(err);
return;
}
//Process data.
})
}
})
The problem is that the second request is not executing.
I am using nodemon to start the express server, but on the nodemon only the first request is receive on the express.
But the strange thing is that when I tried to call the method on the second time without closing the electron app, the second request is executed. And I can see it on the nodemon that the second request is executed first.
The output of the nodemon is like this.
POST /path/to/url 200 6.181 ms - 641 //-> this is the first execution. then there is nothing follows.
// Then I call the method again using the app. And the result is this.
POST /path/to/second/url 200 9.645 ms - 21
POST /path/to/url 200 21.628 - 641
It look like the /path/to/second/url is staying on somewhere nowhere and just send to the server if the method is called for the second time.
Please help, thanks.
Update: Added more info.
I have a folder could routes all the .js file is save there.
then I am loading it using this on the my app.js
let rs = fs.readdirSync(path.join(process.cwd(), '/routes/'));
rs.forEach((file) => {
if (file.indexOf('.js') !== -1) {
let fn = '/' + file.replace(/\.[^/.]+$/, '');
let pt = path.join(__dirname, './routes', fn);
let req = require(pt);
app.use(fn, req);
}
});
Then on the routes files I have something similar like this.
router.post('url', (req, res) => {
// here calling the controller.
// mostly just single line passing the request and result variable to the controller.
});
Actually that requests is called inside the ipc callback. I have a menuitems and on the click() event I just used the browserWindow.getFocusedWindow().webContents.send('ipc-name') then this will be triggered.
ipc.on('ipc-name', () => {
// The request is called here.
}
This does not solve the OP problem as the problem exists in Linux env but acts as an workaround.Instead of request module we have to make use of ClientRequest API in electron which is Event based and only makes the task much more difficult.But doesn't suffer from the issue faced in callback inside callback.
Example Code:
function triggerCall() {
const request = net.request(`${root}/routes/get1`);
request.on('response', (response) => {
response.on('data', (chunk) => {
console.log(`BODY: ${chunk}`)
})
response.on('end', () => {
console.log('req 1 end');
const request1 = net.request(`${root}/routes/get2`);
request1.on('response', (response) => {
response.on('data', (chunk) => {
console.log(`BODY: ${chunk}`)
})
response.on('end', () => {
console.log('req 2 end');
})
})
request1.end();
})
})
request.end();
};

How do I upload a file using multipart/form-data header with native Node.js https.request?

I am trying to upload a file from my computer using native https.request from node.js. My code look like this:
let query = require('https').request({
hostname: 'somehost.com',
path: '/path/to/upload',
headers: {'Content-Type': 'multipart/form-data'},
method: 'POST'
}, (res) => {
let data = '';
res.on("data", (chunk) => {
data += chunk.toString('utf8');
});
res.on('end', () => {
console.log("data");
})
});
query.on("error", (e) => {
console.error(e);
});
query.write(Buffer[
{key1: 1, file: require("fs").createReadStream("/path/to/file.txt")}
]); // I don't know how to put here
query.end();
I don't get any response from host, the file failed to upload. How can I do this?
When uploading multipart/form-data, the Content-Type header must include a boundary, in order to indicate where each "part" lives within the posted data. In order to set the boundary for The Multipart Content-Type, you can use the form-data package from NPM. You could set the header/boundary manually, but the form-data package will handle this for you and free you from having to worry about the details, etc.
In order to use form-data in your example, you will need to do the following:
Create a new FormData object and append the relevant parts:
let formData = new require('form-data')();
formData.append('key1', 1);
formData.append('file', require("fs").createReadStream("/path/to/file.txt"));
Use the getHeaders function to build the correct HTTP headers:
require('https').request({
headers: formData.getHeaders(),
// ...
}
Use pipe in order to allow form-data to process your data and send it to the server:
formData.pipe(query);
With this change, you no longer need your calls to query.write or query.end - the call to pipe takes care of that.
For completeness, here's the final code with the changes I described:
let formData = new require('form-data')();
formData.append('key1', 1);
formData.append('file', require("fs").createReadStream("/path/to/file.txt"));
let query = require('https').request({
hostname: 'somehost.com',
path: '/path/to/upload',
method: 'POST',
headers: formData.getHeaders()
}, (res) => {
let data = '';
res.on("data", (chunk) => {
data += chunk.toString('utf8');
});
res.on('end', () => {
console.log(data);
})
});
query.on("error", (e) => {
console.error(e);
});
formData.pipe(query);

Resources