Problem with interrogation my API with mongoose - express

I have a problem with the result of my query at the database mongoDB.
When I exec my query the result is always empty: [].
I'm new user, please help me.
I use SO Mac OS Catalina.
This code is in the access of my API (I obscured my password)
//connection on mongoDB
let URI =
"mongodb+srv://marco:<xxxxxxxxx>#persona-iydyz.gcp.mongodb.net/test?retryWrites=true&w=majority";
mongoose.connect(URI,
{ useNewUrlParser: true, useUnifiedTopology: true });
mongoose.connection.on('connected', () => {console.log('mongodb connected')})
and this code is in my route who exec the query
var express = require('express');
var router = express.Router();
const persona = require('./../classes/persona')
/* GET home page. */
router.get('/', function(req, res, next) {
let result = persona.find().exec();
res.send(result);
});
module.exports = router;
I don't understand why my result is always empty.
please help me. Thanks

Any mongoose model method should be called with a callback, otherwise it just builds a query, not executes it. exec() is there to execute this built query. You can use callback directly if you do not have to do something with built query.
Then try with the below code:
router.get('/', function(req, res, next) {
persona.find({}, (err, result) => {
if(err)
return res.send(err.message);
return res.send(result)
})
});
If you are using promisified mongoose, then it will be
router.get('/', function(req, res, next) {
try {
let result = await persona.find({});
return res.send(result);
} catch(err) {
return res.send(err.message);
}
});
P.S. Check is the mongoDb is connected successfully or not!

Related

node axios as middleware

I am trying to construct a middleware and then use it within the app.get route.
I know it's looks very "pioneer" but i am learning.... How can io get it to work?
const BooksMiddle = async (req, res, next) => {
axios
.get(`https://www.googleapis.com/books/v1/volumes/? q=${term}&keyes&key=${process.env.GBOOKSKEY}`)
.then((result) => {
const data = result.data;
const books = data.items;
return books;
});
next();
}
module.exports = textMiddle;
app.get("/", textMiddle, (req, res, next) => {
res.render('index');
});
If the point of this middleware is to get some book data and make that available for your template rendering, then you can put that data into res.locals where templates called from res.render() will automatically look for data:
const bookMiddle = async (req, res, next) => {
axios
.get(`https://www.googleapis.com/books/v1/volumes/?q=${term}&keyes&key=${process.env.GBOOKSKEY}`)
.then((result) => {
res.locals.books = result.data.items;
next();
}).catch(next);
}
module.exports = bookMiddle;
And, then after you import bookMiddle, you can use it like this:
app.get("/", bookMiddle, (req, res, next) => {
res.render('index');
});
If you refer to the books data structure in your template, the template engine will look in res.locals.books for that data (where the middleware puts the data).

Express: Get the data sent on finish

I would like to create and express.use a middleware that gets the data sent from all endpoints and use it for caching. But I am unable to get that data with res.on('finish', cb). Is there even such a thing ?
Thank you
Add middleware and override existing res.send function with your custom function like below
app.use((req, res, next) => {
const { send } = res;
res.send = (data) => {
// Store in cache
return send(data);
};
next();
});
I will close my question since I found a way myself :
app.use((req, res, next) => {
const send = res.send;
res.send = (data) => {
res.send = send; // this line is important not to have an infinite loop
// do something with `data`
return res.send(data);
};
next();
});

app.get with multiple arguments - e.g. app.get('/', requireAuth, (req, res) => { })

I am wondering how express.js works when you specify multiple argument in the app.get, for example:
app.get('/', requireAuth, (req, res) => { })
Here, I believe you have the slash "/" as the route, and (req, res) as arguments for the callback function. But there is also the 'requireAuth' part which was used for authorization. But in the documentation I don't find anything about multiple arguments in the way we use 'requireAuth' now. It does mention that you can use an array of functions but that is not used here (https://expressjs.com/en/guide/routing.html). How does it work?
P.S. The 'requireAuth' code for reference:
module.exports = (req, res, next) => {
const { authorization } = req.headers;
// authorization === 'Bearer laksjdflaksdjasdfklj'
if (!authorization) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const token = authorization.replace('Bearer ', '');
jwt.verify(token, 'MY_SECRET_KEY', async (err, payload) => {
if (err) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const { userId } = payload;
const user = await User.findById(userId);
req.user = user;
next();
});
};
All the arguments are simply middle-ware functions that you call before the actual route logic is run(for individual routes). As long as there is next handling in each of the middle-ware functions, the chain will run till your route logic, and then exit where next isn't handled.
This is the magic of express. You can do many things with it, like error handling, handling different content types etc.

Using Node.js / Express is it possible to next() an error from inside an IIFE and advance to the error handling middleware?

Question: Using Express is it possible to return an error from inside an IIFE and advance to my error handling middleware?
Background: The IIFE is used to create an async container to wrap await statements. I can't see a way out of this and I wonder if I'm using the wrong basic, pattern altogether.
Simplified Example:
app.get('/', function(req, res, next) {
(async function() {
try {
let example = await someLogic(x);
} catch(err) {
return next(new Error('oops'));
}
})();
console.log('main endpoint');
});
app.use(function(err, req, res, next) {
console.log('my error', err.message);
});
Using Express is it possible to return an error from inside an IIFE and advance to my error handling middleware?
Yes, that works fine. It will call next(err) just fine. But, your return will return only from the IIFE and the rest of your request handler after the try/catch will still execute (not sure if you want that or not).
FYI, it's probably simpler to declare the request handler as async and then you don't need the IIFE wrapper:
app.get('/', async function(req, res, next) {
try {
let example = await someLogic(x);
console.log('main endpoint');
// send some response here
} catch(err) {
return next(new Error('oops'));
}
});

Supertest and Mongoose Middleware (post remove)

I have been fiddling with this for days, and I cannot figure out why the Mongoose middleware is not being invoked.
So I have an API in node.js and I have a website using Angular.js. The Mongoose middleware is this:
schema.post('remove', function (doc) {
console.log('doctors - post - remove');
});
So this hook is called perfectly fine when invoked from the Angular front end. However, when I run a test with supertest, chai, and mocha the hook is not invoked. Here is my code for the testing:
it('/doctors - POST - (create doctor)', function(done){
request(app)
.post('/doctors')
.send(doctor)
.end(function (err, res){
if (res.body['error']) {
expect(S(res.body['error']).startsWith('doctor already exists')).to.be.true;
}
else
expect(res.body['email']).to.equal(doctor['email']);
done();
});
});
....
it('/doctors/remove - DELETE', function(done){
request(app)
.del('/doctors/remove')
.auth(new_doctor_creds["email"], new_doctor_creds["pass"])
.end(function (err, res){
expect(Object.keys(res.body).length).to.not.equal(0);
done();
});
});
And here is my route for the express app:
app.delete('/doctors/remove', authController.isAuthenticated, function (req, res, next) {
var email = req.user['email'];
Doctors.findOne({email:email}).remove(function (err, removed) {
if (err) return next(err);
return res.status(200).send(removed);
});
});
Again, this Mongoose middleware works perfectly fine when invoked from an API call from the Angular app. However, it does not work when tested with supertest. Any ideas on what to do here?
EDIT: I tried to recreate this example with a simplified version that way you can see all of the code. So here is a two file version that is STILL not working. Here is the app.js:
var mongoose = require('mongoose');
var app = require('express')();
var http = require('http');
var fs = require('fs');
var Doctors = require('./schema');
mongoose.connect('mongodb://localhost/m4', function(err) {
if (err) throw err;
console.log('connected');
app.get('/post', function (req, res, next) {
console.log('create');
Doctors.create({email:"hello"}, function (err, inserted) {
if (err) console.log(err);
res.end();
});
});
app.get('/delete', function (req, res, next) {
console.log('removed');
Doctors.remove({email:"hello"}, function (err, removed) {
if (err) console.log(err);
res.end();
});
});
http.createServer(app).listen('6000', function () {
console.log('now listen on localhost:6000');
});
});
and the schema:
var mongoose = require('mongoose');
var schema = mongoose.Schema({
email: { type: String }
});
schema.pre('save', function (next) {
console.log('doctors - post - save');
next();
});
schema.post('remove', function (doc) {
console.log('doctors - post - remove');
});
module.exports = mongoose.model('Doctors', schema);
Here's what I suggest. Let's perform the #remove on the doc found by #findOne. If I remember correctly, remove post hooks only works on Doc#remove and not on Model#remove.
schema.post('remove', function (doc) {
console.log('doctors - post - remove'); // <-- now runs
});
app.delete('/doctors/remove', authController.isAuthenticated, function (req, res, next) {
var email = req.user['email'];
Doctors.findOne({email: email}, function(err, doc) {
if (err) {
return next(err);
}
doc.remove().then(function(removed) {
return res.status(200).send(removed);
}, function(err) {
next(err);
});
});
});
Mongoose post hooks run AFTER the operation is completed, concurrently with operation callbacks. See the comments below:
Doctors.findOne({email:email}).remove(function (err, removed) {
// All this code and the post hook are executed at the same time
if (err) return next(err);
// Here you send the response so supertest#end() will be triggered
// It's not guaranteed that post remove was executed completely at this point
return res.status(200).send(removed);
});
Post hooks were made to run processes independent of the server response. When you run tests, the server shuts down right after the tests are completed, and maybe it had no time enough to finish the post hooks. In the other hand, when you call the API from a client, normally you keep the server running, so the post jobs can be completed.
Now, there comes a problem: how can we test post hooks consistently? I got up this question because I was looking for a solution to that. If you already have an answer, please post here.