Express : unable to load the intended path with middlewares - node.js

i am beginer with Express and I have a rather strange functionality that I am achieving with the middlewares. Here I call a URL which is fetched by its middleware and then on next() another middleware gets called. now in the next() of the second middleware I need to load the component, but issue is that, URL is not changing after the first middleware's next().
Code :
Express App : Router :
app.use('/common/global/login', mainHandler);
app.use('/common/*', subhandler, SuccessComponent);
Middleware :
export function mainHandler(req, res, next) {
const global-url= "someURL"
if (global-url) {
return fetch(global-url)
.then((response) => response.json())
.then((response) => {
if (response.data) {
next();
} else {
throw Error(response.statusText);
}
})
.catch((error) => {
res.redirect('/session-expired');
next(error);
});
}
res.redirect('/session-expired');
}
export function subhandler (req, res, next) {
const other_url= "someOtherURL"
return fetch(other_url)
.then((response) => response.json())
.then((response) => {
if (response.data) {
// here it not loading the SUCCESSCOMPONENT as the URL still remains /common/global/login
return next();
}
throw Error(response.statusText);
})
.catch((error) => {
next(error);
res.redirect('/session-expired');
});
}
res.redirect('/session-expired');
}

You have a syntax error with your code, it may be worth fixing this first to see if it is contributing to the error you are having:
export function mainHandler(req, res, next) {
const global-url= "someURL"
if (global-url) {
return fetch(global-url)
...
You cannot define a variable that contains a hyphen -, as this read as the subtract operator.
const global-url = ... , should be const global_url = ...
And of course update all instances where you are calling this variable.
In your code's current state, next() is not being called by the first middleware because if (global-url) {...} would not return a thruthy value therefore not triggering the next middleware in the chain.
Try:
export function mainHandler(req, res, next) {
const global_url= "someURL"
if (global_url) {
return fetch(global_url)
.then((response) => response.json())
.then((response) => {
if (response.data) {
next();
} else {
throw Error(response.statusText);
}
})
.catch((error) => {
res.redirect('/session-expired');
next(error);
});
}
res.redirect('/session-expired');
// Note that if this 'if' is not satisfied, 'next()' is not called.
}

Related

Using Firebase Cloud Functions to fetch data from an external API (Timeout)

This is my code:
const functions = require('firebase-functions');
const axios = require('axios');
const cors = require('cors')({ origin: true });
exports.apistatus = functions
.region('europe-west1')
.https.onRequest((req, res) => {
cors(req, res, () => {
if (req.method !== "GET") {
return res.status(401).json({
message: "Not allowed"
});
}
return axios.get('https://api.bsmsa.eu/ext/api/bsm/gbfs/v2/en/station_status')
.then(response => {
console.log(response.data);
return res.status(200).json(
response.data
)
})
.catch(err => {
return res.status(500).json({
error: err
})
})
})
// ! res.end();
});
I'm already using the Blaze Plan but I always get this when I invoke it >> Function execution took 60002 ms, finished with status: 'timeout'.
If I try to terminate the function like the documentation says with res.send() or res.end() I get this error: Error: Can't set headers after they are sent.
It works fine when I try to fetch other APIs.
You need a return keyword before the cors() function call.
exports.apistatus = functions
.region('europe-west1')
.https.onRequest((req, res) => {
return cors(req, res, () => { // <------ RETURN NEEDED HERE
if (req.method !== "GET") {
return res.status(401).json({
message: "Not allowed"
});
}
return axios.get('https://api.bsmsa.eu/ext/api/bsm/gbfs/v2/en/station_status')
.then(response => {
console.log(response.data);
return res.status(200).json(
response.data
)
})
.catch(err => {
return res.status(500).json({
error: err
})
})
})
});
Your axios code is being executed, and I suspect it's receiving a response, but because you aren't returning the result from CORS, the "parent" cloud function never knows the task has finished.
This example shows a sample from the official Firebase GitHub where they're using Cors.

Res.Redirect is not getting called in Jest Test

i am using node-express and am trying to check the redirect when the call from API is successful. I am getting an error that - Expected mock function to have been called, but it was not called.
here is my function:
export function globalAuthHandler (req, res, next) {
const global_signin_url = config.get('url');
if (global_signin_url) {
console.log(global_signin_url);
fetch(global_signin_url)
.then((response) => response.json())
.then((response) => {
console.log('Response', response);
if (response.data) {
console.log('Success!!!');
res.redirect('/signIn');
} else {
console.log('going here 1' + response);
res.redirect('/session-expired');
throw Error(response.statusText);
}
})
.catch((error) => {
console.log('going global here 2 ' + error);
next(error);
});
} else {
console.log('going here 3');
res.redirect('/session-expired');
}
}
here is the test :
it('should throw the error and redirect if the API fails with 404.', async () => {
// Setup
Config.get = jest.fn();
Config.get.mockReturnValue(true);
Config.initialize = jest.fn(() => Promise.resolve({ data: {} }));
const req = jest.fn(),
res = { redirect: jest.fn() },
next = jest.fn();
//global.fetch = jest.fn(() => new Promise((resolve) => resolve({ response: { ok: false, status: 404 } })));
global.fetch = jest.fn(
() =>
new Promise((resolve) =>
resolve({
json: () => {
return { };
}
/* data: { ok: true } */
})
)
);
// Act
await middlewares.globalAuthHandler(req, res, next);
// Assert
expect(res.redirect).toHaveBeenCalled();
expect(res.redirect).toHaveBeenCalledWith('/signIn');
});
I am not able to figure out that - even after going to the success!!! log, redirect is not getting triggered.
Calling await on middlewares.globalAuthHandler doesn't wait for it to complete since it isn't returning the Promise.
Return the Promise created by fetch:
export function globalAuthHandler (req, res, next) {
...
return fetch(global_signin_url) // <= return the Promise
...
}
...and the test will wait for the Promise to resolve before continuing to the expect statements.
That will give res.redirect a chance to be called before it is tested.

POST request for many to many in express

I have 2 tables: user and material which have a m:m relationship. The intersection entity is journalMaterials. I am trying to send a POST request to insert into journalMaterials. Also, this table has 2 attributes: recycledQuantity and recycleDate. I tried something, but if i insert with a materialId which doesn't exist it doesn't give me "not found".
app.post('/users/:uid/materials/:mid', (req, res, next) => {
User.findById(req.params.uid)
.then((user) => {
if (user){
let journalMaterial = req.body
journalMaterial.userId = user.id
Material.findById(req.params.mid)
.then((material) => {
if (material){
journalMaterial.materialId = material.id
return JournalMaterial.create(journalMaterial)
}
else{
res.status(404).send('not found')
}
})}
else{
res.status(404).send('not found')
}
})
.then(() => {
if (!res.headers){
res.status(201).json('created')
}
})
.catch((err) => next(err))
})
I've solved it. Here is the correct code.
app.post('/users/:uid/materials/:mid', (req, res, next) => {
const { uid, mid } = req.params;
Promise.all([
User.findById(uid),
Material.findById(mid)
])
.then(([user, material]) => {
if (user && material) {
let journalMaterial = req.body
journalMaterial.userId = user.id
journalMaterial.materialId = material.id
res.status(201).json('created')
return JournalMaterial.create(journalMaterial)
}
res.status(404).send('not found')
})
.catch(err => next(err));
})
Slightly re-wrote this to make it a bit more readable. Removed your nested promise calls... (let's not dive into promise hell when they try to get rid of callback hell..)
app.post('/users/:uid/materials/:mid', (req, res, next) => {
const { journalMaterial } = req.body;
const { uid, mid } = req.params;
Promise.all([
User.findById(uid),
Material.findById(mid)
])
.then(([user, material]) => {
if (user && material) {
journalMaterial.userId = user.id;
journalMaterial.materialId = material.id;
return JournalMaterial.create(journalMaterial);
}
res.status(404).send('not found');
})
.then(() => {
if (!res.headers) {
res.status(201).json('created');
}
})
.catch(err => next(err));
});
Your check against if(user) currently passes. It seems that if that's what is happening, you're always getting an object back. Lots of databases generally don't simply return a null or false value, but rather an object with a bunch of meta data. In that object is generally the data you requested (ie, user.data.id, but it may be that user.data is NULL). Can you verify what the exact contents of Users is? It's evaluating to truthy, thus it must have something in it.

How to test express middleware that depends on other vendor middleware?

I wanna test a middleware function that inside calls a vendor middleware function. The middleware is:
const expressJwt = require('express-jwt');
const validateJwt = expressJwt({ secret: 'whatever' });
exports.isAuthenticated = (req, res, next) => {
if (req.query && req.query.hasOwnProperty('access_token')) {
req.headers.authorization = `Bearer ${req.query.access_token}`;
}
validateJwt(req, res, next);
};
I've tried to create a sinon.spy() object and pass it as next parameter, but is not called apparently.
Another approach I've tried is to check if exists req.user, since the purpose of the express-jwt middleware is to validate and attach user to the req object. No luck with this neither.
I've seen the existence of chai-connect, but not sure how to use it.
Any ideas? Highly appreciate it!
I finally managed to do it with proxyquire and chai-connect:
In your mocha config:
chai.use(require('chai-connect-middleware'));
global.connect = chai.connect;
In your test:
describe('isAuthenticated', () => {
// Wrap call to chai.connect into a function to use params and return a Promise
const mockedMiddleware = (changeSecret) => {
let oldToken;
if (changeSecret) {
oldToken = acessToken;
acessToken = 'blabalblalba';
}
return new Promise((resolve, reject) => {
connect.use(middleware.isAuthenticated)
.req(req => {
req.query = { access_token: acessToken };
})
.next((res) => {
acessToken = oldToken;
if (res && res.status === 401) {
reject(res.message);
} else {
resolve();
}
})
.dispatch();
});
};
it('should validate correctly', () =>
mockedMiddleware().should.be.fulfilled
);
it('should not validate', () =>
mockedMiddleware(true).should.be.rejected
);
});

Simple get request with node.js and express

I have tried everything and can't figure out what i am doing wrong. I have no problem posting data from the client to the server but the other way around i can't get it to work.
The only response i get in my client is ReadableByteStream {}.
This is my code on the client:
export function getAllQuestionnairesAction(){
return (dispatch, getState) => {
dispatch(getAllQuestionnairesRequest());
return fetch(API_ENDPOINT_QUESTIONNAIRE)
.then(res => {
if (res.ok) {
console.log(res.body)
return dispatch(getAllQuestionnairesSuccess(res.body));
} else {
throw new Error("Oops! Something went wrong");
}
})
.catch(ex => {
return dispatch(getAllQuestionnairesFailure());
});
};
}
This is my code on the server:
exports.all = function(req, res) {
var allQuestionnaires = [];
Questionnaire.find({}).exec(function(err, questionnaires) {
if(!err) {
console.log(questionnaires)
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ a: 1 }));
//res.json(questionnaires)
}else {
console.log('Error in first query');
res.status(400).send(err);
}
});
}
I'm doing some guesswork here, since I'm not sure what flavor of fetch you are currently using, but I'll take a stab at it based on the standard implementation of fetch.
The response inside the resolution of fetch typically does not have a directly readable .body. See here for some straight forward examples.
Try this:
export function getAllQuestionnairesAction(){
return (dispatch, getState) => {
dispatch(getAllQuestionnairesRequest());
return fetch(API_ENDPOINT_QUESTIONNAIRE)
.then(res => {
if (res.ok) {
return res.json();
} else {
throw new Error("Oops! Something went wrong");
}
})
.then(json => {
console.log(json); // response body here
return dispatch(getAllQuestionnairesSuccess(json));
})
.catch(ex => {
return dispatch(getAllQuestionnairesFailure());
});
};
}

Resources