React-Admin displays data from mongoDB but I cannot Edit - node.js

I'm using React Admin for the first time, and my users (coming from mongoDB) are displayed just fine. The problem occurs when I click the "edit" (or "delete") button on a specific user: it says "GET http://localhost:3002/api/users/2a1a3a61-f73b-4a01-b609-ae4bb815f59e 404 (Not Found)"
I use "http://localhost:3002/api/users" to make the GET req to mongoDB: "app.use('/api/users', require('./api/GetUsers'))" and "2a1a3a61-f73b-4a01-b609-ae4bb815f59e" is the id of the user I clicked.
I remember when I first started testing React Admin, that I was using jsonplaceholder.typicode to get data and the edit function was working as well, although, of course, would not persist on refresh.
What am I missing? Is the problem the fact that my api's (http://localhost:3002/api/users) purpose is only getting the data and not post/put also, maybe?
/api/GetUsers
const express = require('express');
const mongoose = require('mongoose');
const ContactUser = require('../DB/ContactUser');
const router = express.Router();
const getUsers = async (req, res) => {
ContactUser.find()
.exec((err, user) => {
if(err){
res.json(err);
} else {
res.setHeader('Access-Control-Expose-Headers', 'Content-Range');
res.setHeader('Content-Range', 'users 0-20/20');
res.json(user);
}
})
};
router.route('/').get(getUsers);
module.exports = router;
/DB/ContactUser
const mongoose = require('mongoose');
const contactUser = new mongoose.Schema({
name: String,
email: String,
message: String,
id: String
});
module.exports = mongoose.model('contactUser', contactUser);

You're missing a second route to retrieve not a list of users, but a single user. It will use the id in the URL to find one user. Something like:
router.get('/:id', function(req, res) {
ContactUser
.findOne({ _id: req.params.id })
.exec((err, user) => err ? res.json(err) : res.json(user));
});

Related

How do I grab information from my mongodb?

I have a schema that's been defined as:
const postSchema = new mongoose.Schema({
url: String,
description: String,
created_date: Date
});
Post = mongoose.model('Post', postSchema)
}
and I have a router that posts and saves it to the database.
router.post('/posts', async function (req, res, next) {
try {
const newPost = new Post({
url: req.body.url,
description: req.body.description,
current_time: req.body.created_date
});
await newPost.save();
res.json('status: success!')
console.log(Post)
console.log(newPost)
} catch (error) {
res.json("status: error! " + error + "could not connect to the database");
}
})
I can see the data in my mongdb and it populates when I enter new data through inputs.
With the code below, the id, url, and description print out when I console log the Post.find();
router.get('/posts', async function (req, res, next) {
let allPosts = await Post.find();
console.log(allPosts)
My question is this, how do I get just the url data out of this? I've tried using:
Post.url
req.query.paramNames
I'm looking through Mongodb documentation but I'm missing something.
Post.find({},{url:1,description:1})
You are doing it wrong. The second parameter should help in selecting the fields you need to find.
This will return an Array. If you are looking for specific document you need to change it to findOne and add the search field on first parameter.

Update record based on username given in Request body

I need to update value in Group db Group_name to the value send in Json payload.
Db schema
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username: String,
Group_name: {
type: String,
default: '',
}
});
mongoose.model('User', UserSchema);
And API request
router.put('/join', async(req, res) => {
try {
const data = await User.updateOne(req.params.username, {
Group_name: req.body.Group_name
});
console.log(data)
res.send({ msg: "Group Updated!!!" })
} catch (err) {
console.error(err.message);
res.sendStatus(400).send('Server Error');
}
});
currently its updating only first record which is incorrect , my requirement is to check for all records based on username given and according to username given in request parameters ,i will update value of Group_name to the value sent in request body.
can anyone help me ?
Modify query condition.
const data = await User.updateOne(
{ username: req.params.username },
{ $set: { Group_name: req.body.Group_name } }
);
First of all, understand the difference between req.body & req.params
req.body means hidden parameters sent in request body like in post or put requests.
req.params means defined paramters in URL. For this, you must have it defined in your route like below
router.put('/join/:username', async (req, res) => {
// ^^^^^^^^ here it is defined, now you can access it like
const username = req.params.username;
//or
const {username} = req.params; // destructuring
}
there is one more thing and that is
req.query means undefined paramters attached to URL with ?/&
If you want to give username without pre defining like /join?username=john then use req.query
router.put('/join', async (req, res) => {
const {username} = req.query;
}
Then you should use updateMany() function instead of updateOne()
try {
const {username} = req.params;
const {Group_name} = req.body;
const data = await User.updateMany(
{username}, // find as many users where username matches
{Group_name} // update group name from body
);
console.log(data);
The consoled data would be like { n: 2, nModified: 2, ...} because the update queries don't return updated documents but status of the query. If you want to get updated record set, you have to query again with find().
// after update
const updatedRecord = await User.find({ username });
console.log(updatedRecord);
::POSTMAN::
Postman has two types of parameters
Params
Body
If you add in Params it will be added in URL /join?username=john#email.com&Group_name=GroupB and you have to access it in code with req.query.username or req.query.Group_name
If you add in Body it will be hidden and can be accessed with req.body.Group_name etc
Hope it helps!

One mongoDB-instance, many DBs - how to connect a user to "their" DB without affecting other users?

I'm building an application (MVC) that will use 4 collections in a DB. When I add new clients to the application they will get their separate DB. So if I have 10 customers, there will be 10 DBs and 40 collections (1 db -> 4 collections)
This way each customer data is seperated from other customers, which is crucial here.
So far I've built the app and everything is working nicely, 'cept one thing.
If Company A logs in and start using the app everything works fine. But when Company A is logged in, and Company B logs in, both Company A AND B will be directed towards Company B:s DB.
I've looked trough my code and I guess this is my own fault because I use .connect (mongoose). When a company logs in the route will fire of a controller that will open a new connection (which overrides the old one) which will redirect all open connections towards that specific DB.
controller.dBlogin.js
mongoose.connect(dbConfig.url + id, options)
.then(() => {
console.log("Successfully connected to the database");
next();
}).catch(err => {
console.log('Could not connect to the database. Exiting now...');
process.exit();
});
The id is fetched from req.params.id (example: http://webapp.com/login/:id).
As far as I've figured .connect only allows one connection at a given time so I need something that will, simply, allow many connections. So I'm thinking that I could use .createConnection for this,
https://mongoosejs.com/docs/connections.html#multiple_connections
But i just cant get it to work.
I changed controller.dBlogin.js to
mongoose.createConnection(dbConfig.url + id, options)
.then(() => {
console.log("Successfully connected to the database");
next();
}).catch(err => {
console.log('Could not connect to the database. Exiting now...');
process.exit();
});
but that only leads to a timeout when the company logs in. How do I use the .createConnections? How do you go from .connect to .createConnection?
Heres the examples of routes, controller, and a model (user-schema).
routes.js
// connect to db, check auth
app.post('/login/:id', dbController.connectDB, dbController.login)
controller.dbLogin.js
exports.**connectDB** = (req, res, next) => {
const id = req.params.id;
// Get Mongoose to use the global promise library
mongoose.Promise = global.Promise;
// Options Conncetion mongodb
const options = {
useNewUrlParser: true,
};
// Connecting to the database
mongoose.connect(dbConfig.url + id, options)
.then(() => {
console.log("Successfully connected to the database");
next();
}).catch(err => {
console.log('Could not connect to the database. Exiting now...');
process.exit();
});
};
exports.login = (req, res, next) => {
passport.authenticate('local-login', {
successRedirect: '/start', // redirect to the secure profile section
failureRedirect: '/login', // redirect back to the signup page if there is an error
failureFlash: true // allow flash messages
})(req, res, next);
};
Example of a model user.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
const Company = require('../models/company.js');
// define the schema for our user model
const userSchema = mongoose.Schema({
local : {
name : {
type: String,
required : true
},
email : {
type : String,
unique : true,
required : true
},
password : String,
active : Boolean,
company : {
type: mongoose.Schema.Types.ObjectId,
ref: 'Company'
}
}
});
// generating a hash for password
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
// checking if password is valid
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};
// create the model for users and expose it to our app
module.exports = mongoose.model('User', userSchema);
So, for the ones who finds themselves in the same spot:
After reviewing my app and the data it will keep I came to the conclusion that there is no need for splitting multi-tenancy. I reworked the app so when the user fetch or write data they only touch "their" data, and this is controlled backend.
But, I did make a post on mongoose github and got an answer,
See post here: https://github.com/Automattic/mongoose/issues/7386
The author gave a great length which seems to have an actually quite nice implementation of this with express and mongoose: http://nmajor.com/posts/multi-tenancy-with-expressmongoose
I hope this is of any help for you, and if you manage to find a solution or something, where you can show some simple code, please post it as there seems to be a lot of people asking about this.
Cheers.

Why is property of user object in mongoose showing as undefined?

This is my Schema:
var UserSchema = new Schema ({
username: String,
password: String,
nativeLanguage: {
type: String,
default: "English"
},
This route prints out the user object to the console.
router.get('/add', isLoggedIn, async (req, res) => {
const Users = await User.find({_id: req.params._id});
console.log(req.user);
res.render('add', {User});
});
What I am trying to do is render specific properties of the user on the add page, to test that I am first trying to access them in the console. Unfortunately User.nativeLanguage comes up as undefined and I don't know why.
User is the model, Users as you named it is the user record.
Lets suppose you export your model.
exports.User = UserSchema;
Then in your route you need.
var user = require('../userModel');
user.find({ _id: req.params._id }, (err, doc) => (
if (!err && doc){
res.render('add', { User: doc});
}
))
Also consider using findOne instead of find if you plan to get only one record.

Mongoose, MongoDb, Node. Mongoose object not registering find()

When i'm getting to my routes and requesting to getUSers from my mongoDB it says the User.find() is not defined. Sorry in advance if i use incorrect terminology i'm jumping in face first.
I'm assuming my routing is done incorrectly somewhere or i didn't include one file somewhere I'm hoping ya'll can help me determine if i'm either storing a file incorrectly in my structure, calling a file at the wrong time, or not initializing a variable correctly? Thanks for the help in advance.
Do i need an additional plugin to read and write to the database?
I keep getting the the following error it says usercontroller.js:20
Error: <!DOCTYPE html><html><head><title></title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>undefined is not a function</h1><h2></h2><pre>TypeError: undefined is not a function
at getUsers (c:\Users\Ravenous\kitchen\routes\user_api.js:5:10)
at c:\Users\Ravenous\kitchen\routes\user_api.js:26:3
file structure in case it helps
-bin
-node_modules-\
-user-app-\
-user.js //this is the model written with mongoose schema
-public-\
-html
-images
-javascripts-\
-userController
-userService //AngularJS factory
-stylesheets
-index.html
-routes-\
-user_api.js //with other useful routes inside.
-views //has some jade view engines that came with express-generator
-app.js
-package.JSON
I have this for the user model -- file: node_modules/user-app/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
user_name: String,
password: String,
email: String,
location: String,
date_created : {type: Date, default: Date.now}
});
var User = mongoose.model('User', UserSchema);
module.exports= {User:User};
I then import user.js file to the ./routes/user_api.js file which looks like this
user_api.js :
var User = require('./node_modules/user-app/user');
function getUsers(res){
User.find().populate('users').exec(function(err, users){
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err){
res.send(err)
}else{
console.log(users[0][0].name)
res.json(users); // return all users in JSON format
db.close();
}
})
};
module.exports = function(app) {
// api -----------------------------------------------------------
// get all users
app.get('/node_modules/user-app/user', function(req, res) {
// use mongoose to get all users in the database
getUsers(res);
});
// create user and send back all users after creation
app.post('/node_modules/user-app/user', function(req, res) {
// create a user, information comes from AJAX request from Angular
User.create({
user_name: User.user_name,
password: User.password,
email: User.email,
location: User.location,
done : false
}, function(err, todo) {
if (err){
res.send(err);
}else{
// get and return all the users after creating one
getUsers(res);
}
});
});
// delete a user
app.delete('/node_modules/user-app/user:user_id', function(req, res) {
User.remove({
_id : req.params.user_id
}, function(err, user) {
if (err)
res.send(err);
getUsers(res);
});
});
};
I'm adding this user_api route and get method to the app.js file like so
// loading some required modules above this
var app = express();
var user = require('./routes/user_api')(app);
to handle the Angular and Node connection and state sharing i'm using the following
userController.js:
(function(){
'use strict'
var userCtrl = angular.module("userController",[]);
console.log('userController init')
userCtrl.controller('UserController', ['$scope','$http','Users',function($scope,$http,Users){
$scope.users = {};
$scope.loading = true;
console.log ("user init");
Users.get()
.success(function(data){
$scope.users = data;
$scope.loading = false;
console.log(data)
})
.error(function(err){
console.log('Error: ' + err);
});
$scope.createUser = function(){
if($scope.users.user_name != false){
console.log($scope.users.user_name)
$scope.loading = true;
Users.create($scope.users)
.success(function(data){
$scope.loading=false;
$scope.users = data;
console.log(data);
})
.error(function(err){
console.log('Error: ' + err);
});
}
};
}]);
})();
Change the line where you require User to
var User = require('./node_modules/user-app/user').User;
This is because in your export statement when you define a user you have the line:
module.exports = {User:User};
So, you are exporting an object that has a User property on it.

Resources