Prevent expressjs from sending text in bare response - express

I am using expressjs to implement api calls for my app. Much of the time I don't want to send back extra data in the body however expressjs sends the http status text in the response if you don't specify a body.
For res.send(200); outputs OK in the body, res.send(400); outputs Not Found and so on.
The problem is that my UI is expecting well formatted JSON or an empty body, and the bare strings break this convention. The work around I've found for now is to send res.send(200,{}); to send an empty object, but that's a pain in the butt to do that for every api call.
Is there any way to get around express returning this bare text on an empty response?

Without modifying the express library itself, you can create a middleware function that extends the functionality of res.send.
You can modify res.send so an empty string is added to its arguments when invoked only with a status code. This will prevent the name of the status code from being sent.
app.use(function(req, res, next) {
var send = res.send;
res.send = function() {
if (arguments.length == 1 && typeof arguments[0] == 'number') {
arguments[arguments.length++] = '';
}
return send.apply(res, arguments);
};
next();
});
app.get('/good', function(req, res) {
res.send(200);
});
app.get('/not-found', function(req, res) {
res.send(404);
});

Related

How to make an HTTP request within router.get method in Express

I'm making a simple API in express js. I've an end point where I'll make a call to GitHub api int turn. My Front-end application will utilize it. Here is my code:
api.js
const express = require('express');
const router = express.Router();
...
var http = require('http');
var https = require("https");
router.get('/github', function (req, res) {
// APPROACH 1: Failed : fetch is not defined
// fetch('https://api.github.com/users/tmtanzeel')
// .then(response => response.json())
// .then(json => console.log(json))
// APPROACH 2: Failed : throw er; // Unhandled 'error' event
/*try {
https.get('https://api.github.com/users/tmtanzeel',function (res) {
console.log(res);
})
} catch (error) {
...
}*/
});
Both my approaches are failing. I've almost no experience with Express. Please picth in
The second method is almost corrent. Add the error handler and send to the caller the data you just received.
https.get('https://api.github.com/users/tmtanzeel',function (apiRes) {
apiRes.pipe(res);
}).on('error', (e) => {
console.error(e);
res.status(500).send('Something went wrong');
});
Handling the response (stream) received from the API call can be done in two ways.
Using pipes which is automatic
Handle read events and manage the data writing manually
I have used the first approach.
However, it is very well recommended that you get sound knowledge in handling Streams if you use Node JS. Streams are the basis of Node JS requests and responses.
https://nodejs.dev/learn/nodejs-streams
You should use Express to handle incoming requests.
(for example if your webapp fetches data from your (express) server).
Read the docs: http://expressjs.com
Your first attempt failed because fetch is an implementation of the web-browser, not one of nodejs.
If you want to use fetch try: https://www.npmjs.com/package/node-fetch
its a well documented, easy to use fetch function.
From your examples, all seems fine except I can't see you sending the returned data to the client.
You can try something similar like adding res.send(data) to send the data and make it available on the /github route

Using vue router BeforeRouteEnter method to wait for http request to complete

Hi I'm trying to make it so that when a user opens a page it won't open until the data from the server is successfully retrieved so that it won't appear after 0.5s or so after the user enters.
To do this I read that I need to use BeforeRouteEnter but I'm having trouble finding information on how to properly use this, especially with waiting for my REST API to complete its request.
Here's the method I want to wait to complete before routing to my new component:
async getThread() {
const response = await postsService.fetchOneThread({
id: this.blockId,
topic: this.topicId,
thread: this.postId
});
this.thread = response.data;
}
so once this.thread = response.data only then do I want the page to display.
An important thing to note is that I am also passing through URL parameters to get the data which is the topic/black/post ID.
Here is my getUrlParam method also
url() {
let x = this.$route.params.topic.split('-');
this.topicId = x[0];
let y = this.$route.params.id.split('-');
this.blockId = y[0];
let post = this.$route.params.thread.split('-');
this.postId = post[1];
this.getThread();
}
Thanks
You need to move getThread inside beforeRouteEnter
beforeRouteEnter: (to, from, next) => {
postsService.fetchOneThread({
id: this.blockId,
topic: this.topicId,
thread: this.postId
}).then( response => {
//store the data somewhere accessible
next()
})
},
A few notes:
I don't think beforeRouteEnter can be async, so I'm using then to get the response
the component is not yet ready, so you can't access it yet, you need to save the information some other place so it can be read by the component. I'd suggest using Vuex for this.
If you decide to use Vuex than you need to add a mutation and call it from the promise's callback.
store.commit('ADD_THREAD', response.data)

Aurelia HttpClient cancel requests

I am trying to build an auto complete component and want to make it cancel unresolved requests to the server while they type.
I can find no documentation around this in the documentation for HttpClient. It mentions it IS cancellable (unlike fetch) but not how. https://aurelia.io/docs/plugins/http-services
Currently I have this which I cobbled together quite blindly, unsurprisingly it doesn't even abort the requests:
async searchTermChanged(newValue, oldValue) {
if (newValue.length < 3)
return;
if (this.promises.length) {
this.promises.forEach(x => x.abort());
//should probably remove too
}
var promise = this.httpClient.post(this.endpoint, { SearchTerm: newValue });
this.promises.push(promise);
var data = await promise;
var response = JSON.parse(data.response);
this.results = response;
}
}
Where can I find out more information on how to make cancellable requests? My google-fu is failing me.
Looks like you can do this:
this.client["pendingRequests"].forEach(request => {
request.abort();
});
I am having to do ["pendingRequests"] as I'm using TypeScript and the array does not seem to be within the definition.
Note: I am also using a scoped HttpClient per autocomplete, so that when it cancels all previous requests it will not accidentally cancel something else that the app is requesting.

Node Express - multer causing controller call failure and vice versa

Routes.js
The below code worked.
var upload= multer({ storage: storage})
app.post('/upload', [ upload.any(), function(req, res) {
console.log(req.body) // form fields
console.log(req.files) // form files
res.status(204).end()
}]);
However, I wanted to call a controller method as well which is located in my file uploadController.js
So I did this, down below.
app.post('/upload', controllers.uploadFiles.upload, [ upload.any(), function(req, res) {
console.log(req.body) // form fields
console.log(req.files) // form files
res.status(204).end()
}]);
However, what happened was that my controller was called but then uploading part failed, i.e the below part wasn't called.
console.log(req.body) // form fields
console.log(req.files) // form files
res.status(204).end()
In conclusion, either one of them (multer or controller) work, both of them don't.
What could be wrong with this?
UPDATE
Tried the below. Only controller gets called. No file uploading done.
app.post('/upload', controllers.dataUpload.upload, [ upload.any(), function(req, res, next) {
console.log(req.body) // form fields
console.log(req.files) // form files
next()
}]);
You haven't called next from the first controller. The execution ends after you call res.end(). If you want the execution to continue to the next route match, you must call next() on the first middle-ware.
You can read more about routing and middlewares in the guide: https://expressjs.com/en/guide/routing.html
Here are some quotes that might be relevant:
You need to call the next function so that the next controller is called:
More than one callback function can handle a route (make sure you specify the next object).
When you call methods on the res object, the execution is terminated and the next controller is ignored:
The methods on the response object (res) in the following table can send a response to the client, and terminate the request-response cycle. If none of these methods are called from a route handler, the client request will be left hanging.
Credits to #squgeim for helping me out with the conceptual part. According to his guidance, I figured out the issue. Below is the working code.
Routes.js
var uploadStorage = multer({ storage: storage})
app.post('/upload', controllers.uploadController.uploadFile, [uploadStorage.any(), function(req, res, next) {
console.log(req.body) // form fields
console.log(req.files) // form files
res.status(204).end()
}]);
uploadController.js
module.exports = {
uploadFile: (req, res, next) => {
//Do whatever you want over here.
next()
},
}
So basically, inside app.post argument #2, I call the controller, do whatever I want and then call next() to proceed forward to app.post arguments #3 which is calling multer and uploading file.

Is there a workaround for express 4.x route('/path') with params support?

I'm using a expressjs 4.x to build a simple api on top of mongodb.
The api needs to serve a few sets of data:
/api/v1/datatype1
/api/v1/datatype2
For each data type, I have CRUD operations (post, get, put, delete).
The api requests would look like this:
POST /api/v1/datatype1
GET /api/v1/datatype1:_id
PUT /api/v1/datatype1:_id
DELETE /api/v1/datatype1:_id
If I create a router params like this:
dataType1ApiRouter.param("entity_id", function (req, res, next, id) {
//async db fetch here by id, then call next with fetched data
//or error if faild request/not found entity.
//let's say req.dataEntity = dataEtity; next();
} );
If I create a route like this:
dataType1ApiRouter.route("/datatype1")
.get(":entity_id", function (req, res, next) {
//expcet req.dataEntity to be fetched by the param filter.
})
.post(function(req, res, next) {
//just create an new dataType1 entity.
});
I am getting a syntax error. The route .get and .post (and other methods like those) expect just one parameter, resulting in an error:
Route.get() requires callback functions but got a [object String]
Is there a way to actually group all the "/datatype1" requests under one url declaration instead of repeating the method("datatype1:entity_id") for each method that requires the ID expect for the post method?
There isn't a clean way to do this with Router.route(), but you might consider doing this with another Router instead of a Route there. Then, you could just mount that sub-router.
Basic example, modifying the code you provided:
var mainRouter = express.Router(),
subrouter = express.Router();
subrouter.param("entity_id", function (req, res, next, id) {
// param handler attached to subrouter
});
subrouter.post('/', function(req, res, next) {
// post handler attached to base mount-point
});
subrouter.get("/:entity_id", function (req, res, next) {
// get handler attached to base mount-point/<id>
});
// here we mount the sub-router at /datatype1 on the other router
mainRouter.use('/datatype1', subrouter);
Note that this requires adding a '/' to the URL, so instead of /api/v1/datatype1[someidhere] it would be /api/v1/datatype1/someidhere