How to create URL for every MongoDB document - javascript

I'm trying to map a URL using express routes to a specific value from every document I have in the database.
For example. In the Schema company.name would return a company name.
Is there a way to set the value of company.name to a unique URL?
The only way I thought of it was creating a loop or using mongoose's .find to iterate through the database and set it to
app.get('/' + name , function (req , res ) {
res.render('company.jade');
});
However I'm not sure how to access the database collections from the routes file.
Using
const Company = require('.models/company');
Doesn't return the database so not sure what to do.
I'm not really hip on Mongoose or node.js yet. Really only need to do this one thing and the rest of the app is done in angular which I'm more familiar with.
Hopefully I made some sense here. I do have code for the app buts it's all basic code from app.js and routes.js. I can add it if it would be helpful.

You are looking for Mongoose findOne query.
const Company = require('.models/company');
app.get('/:name', function(req, res){
Company.findOne({'company.name': req.params.name}) //company.name depends upon the schema you set.
.then(function(company){
if (company)
return res.send({success: true, data: company}); //return company as an object containing the queried company document.
else
return res.send({success: false, error: 'Company not found'});
})
.catch(function(err){
if (err) res.send({success: false, error: err});
});
});

Related

Nodejs controller is being messy

I'm new to javascript, node.js (or backend at all). I am trying to create a controller for the login page requests and I am confused about getting data from the MYSQL table and User Authentication and working with JWT package !
In my Controller, I first check if the user input is available in the user table (with a simple stored procedure), then I compare the database password and the user input, after this I want to create a token and with limited time. (I have watched some tutorial videos about JWT and there is no problem with it), my main problem is to figure out how to write a proper controller with this functions?
I have 2 other questions:
1.Is it the right and secure way to get data from MySQL table inside the route? Or should I create a JS class for my controller? (I'm a bit confused and doubtful here)
2.Assuming that comparePassword() returns true, how can I continue coding outside of the db.query callback function scope? Because I have to execute comparePasssword() inside db.query callback
loginController.js :
const { validationResult } = require('express-validator');
const bcrypt = require('bcrypt');
const db = require('../../sqlConnection')
let comparePassword = (dbPass, inputPass) => {
bcrypt.compare(inputPass, dbPass, function(err, result) {
console.log(result)
});
}
// for get request
exports.getController = (req, res) => {
res.send('login')
}
// for post request
exports.postController = (req, res) => {
let errors = validationResult(req)
if(!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() })
}
// find data from MYSQL table
let sql = `CALL findUser(?)`
db.query(sql, [req.body.username], (err, res) => {
if(err) console.log(err)
//console.log(Object.values(JSON.parse(JSON.stringify(res[0]))))
var data = JSON.stringify(res[0])
data = JSON.parse(data).find(x => x)
data ? comparePassword(data.password, req.body.password) : res.status(400).send('cannot find
user')
})
res.send('post login')
}
login.js :
const express = require('express')
const router = express.Router()
const { check } = require('express-validator');
const loginCont = require('../api/controllers/loginController')
router.route('/')
.get(
loginCont.getController
)
.post(
[
check('username').isLength({min: 3}).notEmpty(),
check('password').isLength({min: 4}).notEmpty()
],
loginCont.postController
)
module.exports = router
In my point of view, looks like there is no easy answer for your question so I will try to give you some directions so you can figure out which are the gaps in your code.
First question: MySQL and business logic on controller
In a design pattern like MVC or ADR (please take a look in the links for the flow details) The Controllers(MVC) Or Actions(ADR) are the entry point for the call, and a good practice is to use these entry points to basically:
Instantiate a service/class/domain-class that supports the request;
Call the necessary method/function to resolve what you want;
Send out the response;
This sample project can help you on how to structure your project following a design pattern: https://riptutorial.com/node-js/example/30554/a-simple-nodejs-application-with-mvc-and-api
Second question: db and continue the process
For authentication, I strongly suggest you to take a look on the OAuth or OAuth2 authentication flow. The OAuth(2) has a process where you generate a token and with that token you can always check in your Controllers, making the service a lot easier.
Also consider that you may need to create some external resources/services to solve if the token is right and valid, but it would facilitate your job.
This sample project should give you an example about how to scope your functions in files: https://github.com/cbroberg/node-mvc-api
Summary
You may have to think in splitting your functions into scoped domains so you can work with them in separate instead of having all the logic inside the controllers, then you will get closer to classes/services like: authenticantion, user, product, etc, that could be used and reused amount your controllers.
I hope that this answer could guide you closer to your achievements.

Node.js flat-cache, when to clear caches

I have a Node.js server which queries MySQL database. It serves as an api end point where it returns JSON and also backend server for my Express application where it returns the retrieved list as an object to the view.
I am looking into implementing flat-cache for increasing the response time. Below is the code snippet.
const flatCache = require('flat-cache');
var cache = flatCache.load('productsCache');
//get all products for the given customer id
router.get('/all/:customer_id', flatCacheMiddleware, function(req, res){
var customerId = req.params.customer_id;
//implemented custom handler for querying
queryHandler.queryRecordsWithParam('select * from products where idCustomers = ? order by CreatedDateTime DESC', customerId, function(err, rows){
if(err) {
res.status(500).send(err.message);
return;
}
res.status(200).send(rows);
});
});
//caching middleware
function flatCacheMiddleware(req, res, next) {
var key = '__express__' + req.originalUrl || req.url;
var cacheContent = cache.getKey(key);
if(cacheContent){
res.send(cacheContent);
} else{
res.sendResponse = res.send;
res.send = (body) => {
cache.setKey(key,body);
cache.save();
res.sendResponse(body)
}
next();
}
}
I ran the node.js server locally and the caching has indeed greatly reduced the response time.
However there are two issues I am facing that I need your help with.
Before putting that flatCacheMiddleware middleware, I received the response in JSON, now when I test, it sends me an HTML. I am not too well versed with JS strict mode (planning to learn it soon), but I am sure the answer lies in the flatCacheMiddleware function.
So what do I modify in the flatCacheMiddleware function so it would send me JSON?
I manually added a new row to the products table for that customer and when I called the end point, it still showed me the old rows. So at what point do I clear the cache?
In a web app it would ideally be when the user logs out, but if I am using this as an api endpoint (or even on webapp there is no guarantee that the user will log out the traditional way), how do I determine if new records have been added and the cache needs to be cleared.
Appreciate the help. If there are any other node.js caching related suggestions you all can give, it would be truly helpful.
I found a solution to the issue by parsing the content to JSON format.
Change line:
res.send(cacheContent);
To:
res.send(JSON.parse(cacheContent));
I created cache 'brute force' invalidation method. Calling clear method will clear both cache file and data stored in memory. You have to call it after db change. You can also try delete specified key using cache.removeKey('key');.
function clear(req, res, next) {
try {
cache.destroy()
} catch (err) {
logger.error(`cache invalidation error ${JSON.stringify(err)}`);
res.status(500).json({
'message' : 'cache invalidation error',
'error' : JSON.stringify(err)
});
} finally {
res.status(200).json({'message' : 'cache invalidated'})
}
}
Notice, that calling the cache.save() function will remove other cached API function. Change it into cache.save(true) will 'prevent the removal of non visited keys' (like mentioned in comment in the flat-cache documentation.

How can I access express request object in Mongoose virtual get func

I tried to find this, but I could not find a solution which will answer my doubt.
I have a virtual mongoose property defined as :
postSchema.virtual('permissions').get() => {
});
What I am trying to achieve is fill this permissions property of string array type, with a list of permissions that user has on each post. This logic is derived through the owner user id, which is stored in the post mongo database and the user id present in the request object of express, which is coming from the requestor.
However, I realised that the request object is not available to virtual method:
postSchema.virtual('permissions').get((req) => {
// req is null.
});
Now, I do have a solution to fix this issue by making a find result to lean at service level.
app.get('/api/posts', (req, res) => {
PostModel.find({}, '-fbEmailAddress', { sort: { created_at: -1 } }).lean().exec(function (err, posts) {
posts.array.forEach(element => {
element.permissions = // write your permission logic here
});
res.send(posts);
});
});
However, if I do so, it will not stop calling . any virtual properties that I have also defined. I loose the opportunity to manipulate firstname, lastname to fullname et. al.
Do you guys have any recommendations to resolve this in a nicest possible way, so that I have an access to the current user id coming in the request object?
Please note that it is an API, so I don't want to introduce any kind of session object.
Cheers
I think you should look at this module https://github.com/othiym23/node-continuation-local-storage
It allows to set context variables in your request middleware and then use them in all functions called from it, e.g. mongoose model handlers.

How do I specify MongoDB key/value parameters in an $http.get with an AngularJS controller?

I'm using the MEAN stack for a web app. In my controller.js, I have the following:
var refresh3 = function() {
$http.get('/users').success(function(response) {
console.log("I got the data I requested");
$scope.users = response;
$scope.contact3 = "";
});
};
refresh3();
This pulls back every object in the "users" collection from my MongoDB. How could I add parameters to this to bring back, for example, only objects where "name" : "Bob" ? I have tried adding parameters in the '/users' parentheses using:
$http.get('/users', {params:{"name":"Bob"}})
I've also tried variants of that, but no luck. Any help would be appreciated!
If your server is receiving the data
(and it should, as $http.get('/users', {params:{"name":"Bob"}}) is correct)
On server side, make use of the query string:
req.query
like so:
app.get('/users', function(req,res){
if(req.query.name){
db.users.find({"name":req.query.name},function (err, docs) { console.log(docs); res.json(docs); });
}
else{
db.users.find(function (err, docs) { console.log(docs); res.json(docs); });
}
});
WHAT WAS THE ISSUE?
You hinted in your comments that your server was set to respond to the app.get('/users') GET request like so:
db.users.find(function (err, docs) {
// docs is an array of all the documents in users collection
console.log(docs); res.json(docs); }); });
So I believe that your angularjs $http get is correct, and your server is receiving the parameters {"name":"Bob"} as it should;
it just doesn't know what to do with them:
all it is set to do is to return the whole collection in the particular case of a app.get('/users') GET request.
HOW TO CONFIGURE YOUR SERVER FOR REST
You do not have to re-invent the wheel on the server.
Rather, you could consider using a middleware to automate the task (in the present case, the task is to issue a proper MongoDb request when you receive a get query with parameters from the client)
e.g. express-restify-mongoose middleware

Node.js Deployd 404 error on dpd.get & 400 error on dpd.put when using id parameter

Basically what the title says - I've been getting a 404 Not Found on all ids that I enter for "id":
dpd.items.get("id", function(results, error) {
console.log(results);
});
And a 400 Bad Request on any value of "id":
dpd.items.put("id",{category:value},function(results, error){
console.log("Record updated");
});
All the id values exist in the Deployd dashboard, and I am able to make get requests using any category parameters OTHER than ID.
Feels like I've tried everything at this point, please help!
This error can occur if you insert documents through a different client than deployd.
From here:
MongoDB uses ObjectIds as the default value for the _id field if the _id field is not specified ... if a client does not add an _id field, mongod will add an _id field that holds an ObjectId.
Although the IDs created by mongoDB are visible in the deployd dashboard, they are not normal strings (like the IDs generated by deployd) and deployd does not find them when it's looking for a string.
Try to run a query like the following with any other mongoDB client (e.g. Robomongo):
db.yourcollection.find({_id: ObjectId("some_id_you_know_exists_in_collection")})
If it does not throw an error, the id is most likely an ObjectId that was not created by deployd.
Unfortunately, there is no easy fix. (At least not for big collections and complicated apps.)
For SMALL collections I'd suggest to just duplicate the data into a new collection and let deployd create new IDs.
Quick, dirty and untested:
dpd.collection.get({}, function(res) {
_.each(res, function(object){
object.oldId = object.id //add id backup
delete object.id
// post new object without id -> deployd creates a new id
dpd.newcollection.post(object, function(res, err) {
if(err) {
console.log(err);
};
if(res) {
console.log(res);
};
})
});
})
You have to decide for yourself if that works for you.
Deployd create crud API of any collection.
Make sure collection name is correct try to use
http://localhost:PORT/items/id .. if this will also give 404 then open
http://localhost:PORT/dashboard/items/events/
and On dashboard goto dashboard/items/events/ this will open ON GET panel, write there console.log(this.query)
and about 400 request , you write the code console.log(this.body) on
http://localhost:PORT/dashboard/items/events/#put-panel
This is the way to debug your API, deployd have some issue but better framework to create API instantly

Categories

Resources