MongoDB Atlas: Cannot GET from API - node.js

I developed a simple API that is supposed to GET data from MongoDB Atlas. Unfortunately, if I directly hit the browser or use Postman, I am getting an error:
Cannot GET /api/rooms/getallrooms
server.js
const express = require('express');
const app = express();
const dbConfig = require('./db');
const roomsRoute = require('./routes/roomsRoute');
app.use('api/rooms', roomsRoute);
const port = process.env.PORT || 5001;
app.listen(port, () => {
console.log(`Server started on port ${port} :)`);
});
roomsRoute.js:
const express = require('express');
const router = express.Router();
const Room = require('../models/rooms');
router.get('/getallrooms', async (req, res) => {
try {
const rooms = await Room.find({});
return res.json({rooms});
} catch (error) {
return res.status(400).json({message: error});
}
});
module.exports = router;
rooms.js (Contains room schema):
const mongoose = require('mongoose');
const roomSchema = mongoose.Schema({
name : {
type: 'String',
required: true,
},
capacity : {
type: 'number',
required: true,
},
contact : {
type: 'number',
required: false,
},
type : {
type: 'String',
required: true,
},
imageURLs : [],
currentBookings : [],
description : {
type: 'String',
required: false,
},
rentPerDay : {
type: 'number',
required: true,
}
},{
timestamps: true,
});
const roomModel = mongoose.model('rooms', roomSchema);
module.exports = roomModel;
My Atlas looks like this:
I have ensured from Network Access that all IP connections are allowed (0.0.0.0/0).
This is my first API so I reckon I'm missing something basic. Can anybody help me figure this out?
Update 1:
By adding "/" in app.use, the error is gone but I'm getting empty response from the DB:
{
"rooms": []
}
Update 2:
After ensuring column names in model and DB are synchronised, still the response I'm getting is empty.

Here:
app.use('api/rooms', roomsRoute);
I think you need to prefix the path with a / like so:
app.use('/api/rooms', roomsRoute);
See the Express documentation for path examples.

The problem is with database URL. By default MongoDB gives the URL of "test" database. Updating the database name fixed the problem for me.

Related

Mongoose MongoError : 11000

I sent a create post signal through Postman. a once time, the post signal was a successfully. but next time it was failed . error message is like this.
Error creating new record : {
"driver": true,
"name": "MongoError",
"index": 0,
"code": 11000,
"keyPattern": {
"RoutineParts.userId": 1
},
"keyValue": {
"RoutineParts.userId": null
}
}
i cant understand the error message,
my post code and user model code is like that ,
// it's post code.
router.post('/',(req,res)=>{
const newRecord = User ({
username : req.body.username,
email : req.body.email,
password : req.body.password
})
newRecord.save((err,docs)=>{
if(!err) {
res.send(docs)
}else {
console.log('Error creating new record : ' + JSON.stringify(err,undefined,2))
}
})
})
// it's user model
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
username : {type:String},
email: { type: String, required: true},
password: { type: String, required: true, trim: true },
created_at : { type: Date, default: Date.now },
updated_at : { type: Date, default: Date.now },
}
)
const User = mongoose.model('User', userSchema);
module.exports = { User }
i can't understand. In fact 'RoutineParts' is a one of model, but i didn't write user's documents. and, controller path is corrected in app.js
how can i solve it?
it's RoutineParts model
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
routine_name : {type:String},
userId : { type: String, required: true},
exercise_name : { type: String},
order : { type: Number},
}
)
const RoutineParts = mongoose.model('RoutineParts', userSchema);
module.exports = { RoutineParts }
and it's app.js contents
// Connecting data to the MongoDB through Mongoose
require('./db')
const express = require('express')
const app = express()
const PORT = 5000
const bodyParser = require('body-parser')
const userRoute = require('./controller/user')
const routineRoute = require('./controller/routine')
const RP_Route = require('./controller/routineParts')
const EX_Route = require('./controller/excersise')
app.use(bodyParser.json())
app.get("/", function (req, res) {
res.status(201).send("<h1>Hey guys! Hello World !!</h1>");
});
app.listen(PORT, function () {
console.log(`start express server on port ${PORT}`);
});
app.use('/Users', userRoute)
app.use('/Routine', routineRoute)
app.use('/Excersise', EX_Route)
app.use('/RoutineParts', RP_Route)
What's going on
I took a look at your full code. The only real problem, which also fits with the error message you are seeing, is the following :
In your ./models/routineParts.js you've set a unique field. In your case userid. E.g. if your create a new routineParts document with userid "AAA" you can not create another document with the same userid "AAA". In short this means, every user can only create 1 single routineParts document.
The first time you did a POST request to your route localhost:5000/RoutineParts it created the first routineParts document. After that, every request will fail, because it already created 1 routineParts document. ( Read here about unique index with mongoose )
See your ./controller/routineParts.js . If you try to do the same request with a different userid it should work.
How to fix
1 : Remove unique: true from your ./models/routineParts Schema.
2 : ⚡ Drop the index . Mongoose most-likey already registered this index and you have to drop it specifically. Otherwise it will always treat the userid field as unique.
3 : You are still in development, so it shouldn't hurt to also drop the RoutineParts collection.
4 : Restart the app and try to hit the POST localhost:5000/RoutineParts endpoint a couple of times. You now should be able to create multiple routineParts documents under the same user.
How to drop the index
That's a different story. This question should help you with that. They are also using mongoose. If your are using the latest version of mongoose there is a method called cleanIndexes. Use this method after you've removed the unique:true from your model.
For solve this isuse you must remove index duplicate email_1 from index menu

Mongoose Connection with MongoDB

I've been working with Mongoose/MongoDB and I realized I don't quite understand how the connection between mongoose and my MongoDB database persists. Let's say I have Node application with a server.js file.
const express = require('express')
const dotenv = require('dotenv')
dotenv.config()
const connectDB = require('./config/db')
connectDB()
const app = express()
app.get('/', (req, res) => {
res.send('API is running...')
})
const PORT = process.env.PORT || 5000
app.listen(
PORT,
console.log(`Server running in ${process.env.NODE_ENV} port ${PORT}`)
)
And then I have my database configuration file where the connection is initiated.
const mongoose = require('mongoose')
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGO_URI, {
useUnifiedTopology: true,
useNewUrlParser: true,
useCreateIndex: true,
})
console.log(`MongoDB Connected: ${conn.connection.host}`)
} catch (error) {
console.error(`Error: ${error.message}`)
process.exit(1)
}
}
module.exports = connectDB
My question is, after the database is connected with mongoose.connect(), how does it persist throughout the rest of the application? Obviously, this allows me to interact with the database in other files, but I'm not quite clear on what's going on "underneath the hood". Let's say I have a Product model
const mongoose = require('mongoose')
const reviewSchema = mongoose.Schema(
{
name: { type: String, required: true },
rating: { type: Number, required: true },
comment: { type: String, required: true },
},
{
timestamps: true,
}
)
const productSchema = mongoose.Schema(
{
user: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
name: { type: String, required: true },
image: { type: String, required: true },
brand: { type: String, required: true },
category: { type: String, required: true },
description: { type: String, required: true },
reviews: [reviewSchema],
rating: { type: Number, required: true, default: 0 },
numReviews: { type: Number, required: true, default: 0 },
price: { type: Number, required: true, default: 0 },
countInStock: { type: Number, required: true, default: 0 },
},
{
timestamps: true,
}
)
const Product = mongoose.model('Product', productSchema)
module.exports = Product
I can then have a route
router.get(
'/',
asyncHandler(async (req, res) => {
const products = await Product.find({})
res.json(products)
})
)
Which would operate on this Product model. My question is, how is mongoose aware of the database still (after the initial connection) that allows it to call methods on this Model that allow it to interact with the database.
I'm not an expert in MongoDB nor JavaScript but I think that you might have misunderstood some concepts.
Mongoose as it says on its website provides a schema-based solution module to your application data.
You use mongoose to connect with the database through its methods, the database is not connected with mongoose.
Dotenv file basically stores your server configuration data within your project
separating it from the code that allows you to display port info, processes etc in a clear way.
Let's start reviewing the first file
Here you create some const of express and dotenv
const express = require('express')
const dotenv = require('dotenv')
After that, you apply the config method to the dotenv const (you can also add that method at the first line)
dotenv.config()
Now you create the connectDB const loading the content from the DB file (which I suppose it's the next one)
const connectDB = require('./config/db')
Calling the method of the class starts the connection with the Database:
connectDB()
Now you use express to make a get request that will be displayed on your root folder and basically will display some basic data from the port and the process (loaded from ENV file)
const app = express()
app.get('/', (req, res) => {
res.send('API is running...')
})
const PORT = process.env.PORT || 5000
app.listen(
PORT,
console.log(`Server running in ${process.env.NODE_ENV} port ${PORT}`)
)
Let's review now the other file.
You create a const from mongoose
const mongoose = require('mongoose')
And establish a connection to your DB
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGO_URI, {
useUnifiedTopology: true,
useNewUrlParser: true,
useCreateIndex: true,
})
console.log(`MongoDB Connected: ${conn.connection.host}`)
} catch (error) {
console.error(`Error: ${error.message}`)
process.exit(1)
}
}
This is the important part, you export that const to the whole project so it can be accessed from different parts of the project that's the reason that allows you to connect with the DB without providing the same credentials every single time, and the same logic applies to dotenv file
module.exports = connectDB
((If you're not familiar with Java just ignore this:))
I was working today with MySQL and Java JDBC API and I created some files that allow me to interact with the DB like you're doing with Mongoose, see:
//variables to get DB info and connection
private Connection connection;
private PreparedStatement stmt = null;
private ResultSet rs = null;
private Statement statement = null;
//Constructor, when you create an object of this class will establish a connection
with the dbCredentials variables (like dotenv)
public DevOps() throws SQLException {
this.connection = DriverManager.getConnection(dbCredentials.getDbname(), dbCredentials.getUsername(), dbCredentials.getPass());
}
This would be like your dotenv file, you just have to change the data here instead of changing lots of lines of code when you migrate your DB or update credentials
public class dbCredentials {
public final static String username = "user";
public final static String pass = "password";
public static String dbname = "jdbc:mysql://192.168.56.1/school";
public static String allowMultiQueries() {
return dbname+"?allowMultiQueries=true";
}
public static String getUsername() {
return username;
}
public static String getPass() {
return pass;
}
public static String getDbname() {
return dbname;
}
}
And you basically just have to do this:
DevOps devops = new DevOps();
which would be similar to:
const connectDB = require('./config/db')
So basically the concept of all of this is to keep your code organized, ofc you can have all of that on a single file but in terms of scalability would be painful to maintain especially when you have to apply any change to your DB.
Hope this was useful for you, just ask me if you have any doubt!

"Mongo Error : Authentication failed" can't read datas from MongoDB Atlas

Today I wanted to use clusters in MongoDB Atlas to get an online DB, instead of my MongoDB local database (which worked perfectly fine),
So, I followed a mLab tutorial,
It works perfectly on writing in the database (when I auth in my website, it adds the datas in the database, when I write a message in the chat it adds the message, etc...)
But when I want to read these datas, I got :
MongoTimeoutError: Server selection timed out after 30000 ms
MongoError: Authentication failed
The connect in my server/index.js seems to work, because I got the console log :
mongoose
.connect(
`mongodb+srv://${process.env.USER}:${process.env.PASSWORD}#ofilms-demo-f9iwz.mongodb.net/test?retryWrites=true&w=majority`,
{ useNewUrlParser: true, useUnifiedTopology: true }
).then(() =>
console.log(
"working"
)
)
.catch(err => console.log(err));
but not other routes, like this one (which get all users in the database) :
const mongo = require("mongodb").MongoClient;
router.get("/getAll", function(req, res) {
console.log("get all users");
const client = new mongo(`mongodb+srv://${process.env.USER}:${process.env.PASSWORD}#ofilms-demo-f9iwz.mongodb.net/test?retryWrites=true&w=majority`, {
useNewUrlParser: true,
useUnifiedTopology: true
});
client.connect(err => {
const collection = client.db("test").collection("users");
collection.find().toArray((err, items) => {
res.json(items);
});
client.close();
});
});
One model from Mongoose :
/* eslint-disable no-undef */
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true,
lowercase: true
},
password: {
type: String,
required: true
},
firstname: String,
lastname: String,
sexe: String,
mobilePhone: String,
departement: Number,
city: String,
moviesLiked: Array,
seriesLiked: Array,
moviesDisliked: Array,
seriesDisliked: Array,
moviesFavorites: Array,
seriesFavorites: Array,
lists: Array,
creationDate: {
type: Date,
default: Date.now
},
lastConnection: Date,
isVerified: Boolean,
isAdmin: Boolean,
isModerator: Boolean,
isConnected: Boolean
});
module.exports = User = mongoose.model("users", UserSchema);
I can show you the code of other files if needed, or give you the link of the repo if someone wants it, but it's a really big project,
Thanks,
You seem to connect mongodb with both mongoose.connect() and MongoClient.connect(), one of them will be enough.
If you want to use mongoose you can connect to mongodb in your main file (index.js or app.js), and when connected to the db your server can start listening.
And you don't need to connect mongodb in your routes.
For example:
index.js (main file)
const express = require("express");
const app = express();
require("dotenv").config();
const users = require("../routes/users"); //todo: correct the users route path
app.use("/api/users", users);
mongoose
.connect(
`mongodb+srv://${process.env.USER}:${process.env.PASSWORD}#ofilms-demo-f9iwz.mongodb.net/test?retryWrites=true&w=majority`,
{ useNewUrlParser: true, useUnifiedTopology: true }
)
.then(() => console.log("working"))
.catch(err => console.log(err));
in your route: (users.js)
const express = require("express");
const router = express.Router();
const User = require("../../models/User");
router.get("/users", async (req, res) => {
const users = await User.find({});
res.send(users);
});
module.exports = router;
As you see there is no connection related code in our route, because we have already connected when the application starts.
For this code to work, you need to add your local IP to the IP whitelist IP in mongodb atlas panel. (SECURITY --> Network Access --> IP Whitelist.
Also the user you are using to connect must have read an write priveleges.
You can check your users priveleges in SECURITY --> Database Access --> MongoDB Users.

Filter moongose results by reference field using express

I need filter the products of a collection by category id which is a reference field.
product.js
const restful = require('node-restful')
const mongoose = restful.mongoose
const productSchema = new mongoose.Schema({
name: { type: String, required: true },
category: {type: mongoose.Schema.Types.ObjectId, ref: 'CategoryProduct'}
})
productSchema.pre('find', function () {
this.find().populate('category')
})
module.exports = restful.model('product', productSchema)
routes.js
const express = require('express')
const auth = require('./auth')
module.exports = function (server) {
const protectedApi = express.Router()
server.use('/api', protectedApi)
const Product = require('../api/product/productService')
Product.register(protectedApi, '/products')
}
If I run this on Postman, http://localhost:3003/api/products/?name__regex=/test/i, I can get all products which contains 'test' on name.
So I try get all products by a specific category doing this, http://localhost:3003/api/products/?category=5af3ac4372edc6000468d766.
But as the category is an objectID, I receive this error:
{
"message": "Cast to ObjectId failed for value \"5\" at path \"category\" for model \"SimpleProduct\"",
"name": "CastError",
"stringValue": "\"5\"",
"kind": "ObjectId",
"value": 5,
"path": "category"
}
How do I filter the products by category? I do not know how to treat this parameter correctly and pass to mongoose
Here is my CategoryProduct.js file
const restful = require('node-restful')
const mongoose = restful.mongoose
const categorySchema = new mongoose.Schema({
name: {type: String, required: 'O campo Categoria é obrigatório'},
status: {type: Number, required: true},
updated_at: {type: Date}
})
module.exports = restful.model('CategoryProduct', categorySchema)
you would have to do the following in your route:
const mongoose = require('mongoose');
router.get('/', (req, res, next) => {
const category = req.query.category; // assign the query param for use
// mongoose query now
Product.find({category: mongoose.Types.ObjectId(category)}, (err, result) => {
if (err) {console.log(err)};
if (result) {
res.json(result);
}
});
});
This is just a basic example. Since you are using node-restful you might have to adjust the code but the main idea here is that you require the mongoose module using
const mongoose = require('mongoose')
then pass your category query param to it to convert the string to objectID using:
mongoose.Types.ObjectID(YOUR_CATEGORY_PARAM_HERE)
You can do this anywhere, in the your routes.js or in your express.js, just have to have the string you want to convert :)
hope that helps :).

Mongoose add the object without the values on mongoDB

I have this JS code, it`s a server that will have CRUD functions
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var options = {
db: { native_parser: true },
server: { poolSize: 5 }
};
mongoose.connect('mongodb://localhost/agenda', options);
var Schema = mongoose.Schema;
var contatoSchema = new Schema({
nome: String,
telefones: [],
createdOn: {type: Date, default: Date.now}
});
var Contato = mongoose.model("Contato", contatoSchema);
app.use(bodyParser.json());
app.use(express.static('public'));
app.post('/adicionar', function(req, res){
var contato = req.body;
contato = new Contato({nome: contato.nome, telefones: contato.telefones});
contato.save(function(err, data){
if (err) console.log(err);
else res.json(data);
});
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
when i try to add an object using the URL '/adicionar' on Postman:
{
"nome": Vinicius,
"telefones": [
123456
]
}
he gives me this answer
{
"__v": 0,
"_id": "57439973e033c9902174cad0",
"createdOn": "2016-05-23T23:59:47.284Z",
"telefones": []
}
the problem is that the function doesn`t pass the correct values on the fields 'nome' and 'telefone', but he creates the array 'telefones' on mongoDB, i have the database 'agenda' created too, and he also creates the collection 'contato'.
i tried to rewrite the code search for syntax errors, i think that the bodyParser or Express mondule isn`t working but i installed both on the folder.
Other note, when i change the values of nome: contato.nome, telefones: contato.telefones to nome: "vinicius", telefones: [1235312], and run the code as node application he creates the object in the mongo DB with these values.
you need request as Json
{
"nome": "Vinicius",
"telefones": [
123456
]
}
try define your Schema like this
var contatoSchema = new Schema({
nome: String,
telefones: [Number],
createdOn: {type: Date, default: Date.now}
});
i was trying to pass the json object as a Text in postman
http://imgur.com/w5hYkgw
now the code is working, sorry for that

Resources