I'm trying to create a REST API using Node.js that would fetch the last N rows of a MongoDB collection. This is my current code:
var express = require("express");
var app = express();
var bodyParser = require("body-parser");
var router = express.Router();
var mongodb = require("mongodb");
var MongoClient = require("mongodb").MongoClient;
var db;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({"extended" : false}));
// Initialize connection once
MongoClient.connect("mongodb://localhost:27017/sample", function(err, database) {
if(err) return console.error(err);
db = database;
// the Mongo driver recommends starting the server here because most apps *should* fail to start if they have no DB. If yours is the exception, move the server startup elsewhere.
});
// Reuse database object in request handlers
router.get("/", function(req, res, next) {
db.collection("samplecollection").find({}, function(err, docs) {
if(err) return next(err);
docs.each(function(err, doc) {
if(doc) {
console.log(doc);
}
else {
res.end();
}
});
}).limit(10,function(e,d){});
});
app.use('/',router);
app.listen(3000);
console.log("Listening to PORT 3000");
This is successfully printing out all of the contents of the database on the server console (Whenever the client makes a get request). However, how do I give this JSON information to the client making the GET call instead in an efficient way (and one that supports the situation where the client can add a parameter N that would only fetch the last N rows of the database). I looked into Mongoose and that seemed pretty solid but in my case I already have a pre-existing database and collection so wasn't sure if that was the best route for this task.
If you need any more info or clarification, feel free to let me know! Thanks for reading!
Instead of res.end, you would use res.send and send the response back to the front end.
router.get("/", function(req, res, next) {
db.collection("samplecollection").find({}, function(err, docs) {
if(err) return next(err);
docs.each(function(err, doc) {
if(doc) {
console.log(doc);
var response = {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.parse(doc)
}
res.send(response);
}
});
});
});
To get the last N records of a collection, you could use a combination of sort and limit. First, sort on a specific field such as date in ascending/descending order and then limit the results to whatever N is. Something like this:
db.collection.find({ query }).sort({ key: 1 }).limit(N)
UPDATE:
Based on our ongoing comment conversation, here is an example of how I have successfully sent data back to the client. This does not include the limit or anything fancy.
var express = require('express');
var app = express();
var port = process.env.PORT || 3000;
var db = require('./config/db');
var bodyParser = require('body-parser');
app.use(express.static(__dirname + "/public"));
app.use(bodyParser.json());
app.get('/', function(req, res) {
db.find({}, function(err, data) {
if (err) {
console.log(err);
return res.send(500, 'Something Went wrong with Retrieving data');
} else {
// console.log(data[0]);
res.json(data);
}
});
});
app.listen(port);
console.log('Server listening on port: ', port);
Ended up fixing my issue. Changed my get statement to this:
router.get("/", function(req, res, next) {
db.collection("samplecollection", function(err, collection){
collection.find({}).limit(10).toArray(function(err, data){
res.json(data);
})
});
});
Related
I am trying to pass some coordinates from mysql database to be marked on a map but am having trouble getting them. i have looked at a number of similar questions on stackoverflow and have not been able to find an answer. Would appreciate if someone could please point out where i have gone wrong.
getListings.js
var mysql = require('mysql');
config = {
host: 'localhost',
user: 'root',
password: 'password',
database: 'xx',
port: 'xxxx',
};
var connection = mysql.createConnection(config);
connection.connect(function (err) {
if (err) {
console.log('error connecting:' + err.stack);
}
console.log('connected successfully to DB.');
connection.query('SELECT listing_coords FROM listings', (err, rows) => {
if (err) throw err;
console.log('Data received from Db:\n');
var results = JSON.parse(JSON.stringify(rows));
module.exports = { results };
console.log(results);
});
});
Then in my script.js file i have
var { results } = require('./getListings');
console.log(results);
I am getting an error in the browser console saying "require is not defined"
I will need to figure out how to pull the coordinates from mysql in order to plot them, there has to be a way? Do i have to build an api and use ajax? Thanks in advance for any help.
have updated my getListings.js file - it now displays in the string of data i need in the browser as a raw data packet
var mysql = require('mysql');
const express = require('express');
var app = express();
const bodyparser = require('body-parser');
app.use(bodyparser.json());
config = {
host: 'localhost',
user: 'root',
password: 'password',
database: 'xx',
port: 'xxxx',
};
var connection = mysql.createConnection(config); //added the line
connection.connect(function (err) {
if (err) {
console.log('error connecting:' + err.stack);
}
console.log('connected successfully to DB.');
app.listen(5000, () => console.log('express server is running at 5000'));
app.get('/listings', (req, res) => {
connection.query(
'SELECT listing_coords FROM listings',
(err, rows, fields) => {
if (!err) res.send(rows);
else console.log(err);
}
);
});
I have been unsuccessful to get the output to run in script.js. I will post the working code when its working.
I am getting an error in the browser console saying "require is not defined"
It is because require is not an API for frontend. It should be the syntax of backend(eg. nodeJS).
Do i have to build an api and use ajax?
If you wanna to send data from frontend to backend. Using ajax is possible but the main point is you need to have a backend server(eg. using Express module for nodeJS) to connect with the database(eg. mysql, postgresSQL).
Update on 14 Feb, 2021
My practise for doing this is to use ajax to send a request to the backend server from frontend.
//frontend
$.ajax
({
url: "yourBackendServerUrl", //eg. localhost:8001/listing. Need modification based on your backend setting.
})
.done(function(data) {
console.log(data) //data should be the result from backend, then you can retrieve the data for frontend usage
});
For the CORS problem, you can install cors package. If you have a middleware in the global scope(a.k.a. app.use(cors()) ). Every time there are request, this middleware will run.
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors()) // pay attention to this line of code.
app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
The solution that I got to work is as follows:
getListings.js (this was nodeJS)
var mysql = require('mysql');
const express = require('express');
const bodyparser = require('body-parser');
var app = express();
app.use(bodyparser.json());
**app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept, Authorization'
);
next();
});**// I believe this is a middleware function
config = {
host: 'localhost',
user: 'root',
password: 'password',
database: 'xx',
port: 'xxxx',
};
var connection = mysql.createConnection(config); //added the line
connection.connect(function (err) {
if (err) {
console.log('error connecting:' + err.stack);
}
console.log('connected successfully to DB.');
});
app.listen(5000, () => console.log('express server is running at 5000'));// this can be any port that isnt currently in use
app.get('/listings', (req, res) => {
connection.query(
'SELECT listing_coords FROM listings',
(err, rows, fields) => {
if (!err) res.send(rows);
else console.log(err);
}
);
});
in my script.js file i was missing the following
$.get('http://localhost:5000/listings', function (data, status) {
console.log('Cords Data', data);
I believe that is the jQuery ajax
Then in the heading of my html I needed the following
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type = "module" defer src="script.js"></script>
thanks to everyone that helped me with this. especially #tsecheukfung01.
I dont fully understand all of the pieces so it will take me a while to fully understand this and be able to recreate this on my own. All part of the journey!
I am new to web development in general. I am trying to insert and find documents in a local mongoDB database through GET and POST requests. I am doing this with a Node.js/Express web server. I've managed to get something working, but I'm not sure if I'm doing the right thing.
First, I wrote client.html to send POST requests.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Client</title>
</head>
<body>
<p>Insert something into the database:</p>
<form action="/items" method="POST">
<input type="text" name="item">
<button type="submit">Submit</button>
</form>
</body>
</html>
After reading the mongoDB documentation for Node.js, I decided to write my Node.js server as follows, putting all my routes under one connection (approach 1):
const express = require("express");
const app = express();
const port = 5000;
const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
const url = 'mongodb://localhost:27017';
const client = new MongoClient(url, { useUnifiedTopology: true });
app.use(express.urlencoded({ extended: true }));
client.connect(function(err) {
assert.equal(null, err);
console.log("Connected successfully to server");
const db = client.db("my_db");
const itemsCollection = db.collection("items");
app.get("/", function(req, res) {
res.sendFile(__dirname + "/client.html");
});
// GET request - find documents
app.get("/items", function(req, res) {
itemsCollection.find().toArray(function(err, docs) {
assert.equal(null, err);
console.log(docs);
});
});
// POST request - insert document
app.post("/items", function(req, res) {
itemsCollection.insertOne(req.body, function(err, result) {
assert.equal(null, err);
res.redirect("/");
});
});
app.listen(port, function() {
console.log(`Listening on port: ${port}`);
});
});
However, I've also seen people create a new connection for every route, which would be something like this (approach 2):
const express = require("express");
const app = express();
const port = 5000;
const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
const url = 'mongodb://localhost:27017';
const client = new MongoClient(url, { useUnifiedTopology: true });
app.use(express.urlencoded({ extended: true }));
app.get("/", function(req, res) {
res.sendFile(__dirname + "/client.html");
});
// GET request - find documents
app.get("/items", function(req, res) {
client.connect(function(err) {
assert.equal(null, err);
console.log("Connected successfully to server");
const db = client.db("my_db");
const itemsCollection = db.collection("items");
itemsCollection.find().toArray(function(err, docs) {
assert.equal(null, err);
console.log(docs);
});
});
});
// POST request - insert document
app.post("/items", function(req, res) {
client.connect(function(err) {
assert.equal(null, err);
const db = client.db("my_db");
const itemsCollection = db.collection("items");
itemsCollection.insertOne(req.body, function(err, result) {
assert.equal(null, err);
res.redirect("/");
});
});
});
app.listen(port, function() {
console.log(`Listening on port: ${port}`);
});
Both approaches successfully complete the database operations. I would like to know:
Which of these approaches (if any) are recommended, and why?
Should I be calling client.close() anywhere in my server code?
Any help is appreciated, thanks!
I am trying to learn REST API. I created POST method but it is not working
get method is working fine in postman but post method is not working. Can anyone help me where I am missing?
I am stuck in it.
here is my code
app.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
//connect to mongoose
Genre =require('./models/genre');
Book =require('./models/book');
// Connect to Mongoose
mongoose.connect('mongodb://localhost/bookstore',{ useNewUrlParser: true });
var db = mongoose.connection;
app.get('/', (req, res) => {
res.send('Please use /api/book or /api/genres');
});
app.get('/api/genres', (req, res) => {
Genre.getGenres((err, genres) => {
if(err){
throw err;
}
res.json(genres);
});
});
app.post('/api/genres', (req, res) => {
var genre = req.body;
Genre.addGenre(genre, (err, genre) => {
if(err){
throw err;
console.log(err);
}
res.json(genre);
});
});
app.listen(3000);
console.log("running on port 3000..");
models/genre.js
const mongoose = require('mongoose');
// Genre Schema
const genreSchema = mongoose.Schema({
name:{
type: String,
required: true
},
create_date:{
type: Date,
default: Date.now
}
});
const Genre = module.exports = mongoose.model('Genre', genreSchema);
// Get Genres
module.exports.getGenres = (callback /* we can access through routes*/, limit) => {
Genre.find(callback).limit(limit);
}
//add genre
module.exports.addGenre = (genre, callback) => {
Genre.create(genre, callback);
}
get method is working fine in postman but post method is not working. Can anyone help me where I am missing?
You need to update your code accordingly:
Add in app.js:
// BodyParser middleware
const BodyParser = require(`body-parser`);
// Create application/json parser
App.use(BodyParser.json({ limit: `50mb` })); // Set request size
// create application/x-www-form-urlencoded parser
App.use(BodyParser.urlencoded({ limit: `50mb`, extended: true }));
Update API call:
app.post('/api/genres', (req, res) => {
var genre = req.body;
Genre.addGenre(genre, (err, genreDB) => {
if(err){
throw err;
console.log(err);
}
res.status(200).send(genreDB);
});
});
Hope this works for you.
If you want to access req.body in your POST handler, you'll need to use some express middleware to actually parse the request body. For example, for parsing JSON bodies, you'll need express.json middleware.
I get a message Request is not finished yet and no data will be sent, if I do patch and delete requests in my app ( the post and get request work well)
Here are my requests
In service (front, Angular 4) I create the requests
api = 'http://localhost:3000/tasks';
deleteData(id) {
return this.http.delete( this.api, id);
}
patch(data) {
return this.http.patch( this.api, data);
}
And then call them in component
this.deleteData(this.checkedItems);
this.service.patch(result.data).subscribe(d => {
this.tasks = d;
});
The service
The PATCH request get req.body via console.log - so it should works, but it doesn't
The DELETE request doesn't get any data! The req.body is empty! But I need to pass the array of ids, so I can't do it via params.
Could you please help me or give a hint? Here is my service
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
var Schema = mongoose.Schema;
const app = express();
//Middleware for CORS
app.use(cors());
app.use(express.json());
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));
var todoSchema = new Schema({
taskName: String,
createdAt: Date,
isDone: Boolean,
prioraty: String
}, {
collection: 'tasks'
});
var Model = mongoose.model('Model', todoSchema);
//replace when DB is online
mongoose.connect('mongodb://localhost:27017/admin').then(() => {
console.log("connected");
}).catch (() => {
console.log("not connected");
});
mongoose.connection.once('open', function () {
console.log('mongodb connected.');
});
app.patch('/tasks', function (req, res) {
console.log(req.body);
var updateObject = {
'taskName': req.body.taskName,
'isDone': req.body.isDone,
'prioraty': req.body.prioraty
}
var id = req.body._id;
Model.collection.update({_id : id}, {$set: updateObject});
});
app.delete('/tasks', function(req,res){
console.log('Delete', req.body);
var ids = [];
for (let i = 0; i < req.body.length; i ++) {
ids.push(req.body[i]._id);
}
var myquery = { _id: { $in: ids } };
Model.collection.deleteMany(myquery, function(err, obj) {
if (err) throw err;
});
});
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));
You need to close the connection when you're done handling the request, otherwise the client will wait for the server to send a response until the request timeout is reached.
app.patch('/tasks', function (req, res) {
...
Model.collection.update({_id : id}, {$set: updateObject}, function (err) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
res.sendStatus(200);
});
});
app.delete('/tasks', function(req,res){
...
Model.collection.deleteMany(myquery, function(err) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
res.sendStatus(200);
});
});
As for the DELETE request not having a req.body, that's because Angular 4's http client doesn't allow a body for DELETE requests. Its API for DELETE requests looks like this: this.http.delete(url, httpOptions), with no body support. You'll have to use query parameters if you need to send an array of ids. Query params does support arrays, they look something like this: https://myurl.xyz/tasks?ids[]=1&ids[]=2&ids[]=3
See https://angular.io/guide/http#url-parameters
I am having problems trying to access the "DB" database object that is created when the MongoDB client module connects to my MongoDB database.
At the moment I am getting an error stating that, within data.js, 'db' is not defined. I understand why this is - the db object is not being "passed" through to the router and then subsequently through to the controller.
What is the best way to do this?
I have tried to pass the "db" object through to the router (dataRoutes.js) but I cannot figure how to make this accessible to the controller (data.js). Could someone please help?
Please note I have not included the other routes and controllers but they simply submit a Form via the POST method to /data/submit . The controller below is meant to write this form data to the MongoDB database.
Here is the relevant code:
app.js
var express = require('express');
var path = require('path')
var MongoClient = require('mongodb').MongoClient;
var bodyParser = require('body-parser');
var app = express();
var routes = require('./routes/index');
var dataRoutes = require('./routes/dataRoutes');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
MongoClient.connect("mongodb://localhost:27017/m101", function(err, db) {
if(err) throw err;
console.log("Successfully connected to MongoDB.");
app.use('/', routes); // Use normal routes for wesbite
app.use('/data', dataRoutes);
app.get('/favicon.ico', function(req, res) {
res.send(204);
});
app.use(function(req, res, next) {
var err = new Error('Oops Page/Resource Not Found!');
err.status = 404;
next(err); //Proceed to next middleware
});
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
// update the error responce, either with the error status
// or if that is falsey use error code 500
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
app.use(function(err, req, res, next) {
console.log('Error');
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
var server = app.listen(3000, function() {
var port = server.address().port;
console.log("Express server listening on port %s.", port);
});
});
dataRoutes.js
// router
var express = require('express');
var router = express.Router();
// controller references
var ctrlsData = require('../controllers/data');
router.post('/submit', ctrlsData.submit);
module.exports = router;
data.js
var MongoClient = require('mongodb').MongoClient;
var sendJsonResponse = function(res, status, content) {
res.status(status);
res.json(content);
};
module.exports.submit = function(req, res) {
var title = req.body.title;
var year = req.body.year;
var imdb = req.body.imdb;
/*
console.log('submitted');
console.log(req.body);
sendJsonResponse(res, 201, {title,year,imdb});
*/
var title = req.body.title;
var year = req.body.year;
var imdb = req.body.imdb;
if ((title == '') || (year == '') || (imdb == '')) {
sendJsonResponse(res, 404, {
"message": "Title, Year and IMDB Reference are all required."
});
} else {
db.collection('movies').insertOne(
{ 'title': title, 'year': year, 'imdb': imdb },
function (err, r) {
if (err) {
sendJsonResponse(res, 400, err);
} else {
sendJsonResponse(res, 201, "Document inserted with _id: " + r.insertedId + {title,year,imdb});
}
}
);
}
};
Create a db variable that reference mongodb in app.js :
MongoClient.connect("mongodb://localhost:27017/m101", function(err, db) {
app.db = db;
//.....
});
In data.js, access db from req.app :
module.exports.submit = function(req, res) {
req.app.db.collection('movies').insertOne({ 'title': title, 'year': year, 'imdb': imdb },
function(err, r) {}
)
};
The accepted answer isn't quite correct. You shouldn't attach custom objects to the app object. That's what app.locals is for. Plus, the accepted answer will fail when using Typescript.
app.locals.db = db;
router.get('/foo', (req) => {
req.app.locals.db.insert('bar');
});
Sure, it's longer. But you get the assurance that future updates to ExpressJS will not interfere with your object.
I understand that the answer of #Bertrand is functional, but it is not usually recommended. The reason being that, from a software point of view, you should have a better separation in your software.
app.js
var express = require('express');
var path = require('path')
var MongoClient = require('mongodb').MongoClient;
var bodyParser = require('body-parser');
var app = express();
var routes = require('./routes/index');
var dataRoutes = require('./routes/dataRoutes');
var DB = require('./db.js');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
DB.Init("mongodb://localhost:27017/m101")
.then(() => {
console.log("Successfully connected to MongoDB.");
app.use('/', routes); // Use normal routes for wesbite
app.use('/data', dataRoutes);
app.get('/favicon.ico', function(req, res) {
res.send(204);
});
var server = app.listen(3000, function() {
var port = server.address().port;
console.log("Express server listening on port %s.", port);
});
})
.catch((e) => {
console.log("Error initializing db");
});
db.js
var _db = null;
module.exports = {
Init: (url) => {
return new Promise((resolve, reject) => {
if (!url)
reject("You should provide a URL");
MongoClient.connect("mongodb://localhost:27017/m101", function(err, db) {
if(err) reject(err);
_db = db;
resolve(); // Or resolve(db) if you wanna return the db object
});
});
},
Submit: (req, res, next) => {
// Whatever goes. You have access to _db here, too!
}
};
in data.js
var DB = require('../db.js');
router.post('/submit', DB.submit);
Finally, even this answer can be improved as you are not usually advised to wait for the DB to connect, otherwise, you are losing the advantage of using ASync procs.
Consider something similar to here in app.js:
Promise.resolve()
.then(() => {
// Whatever DB stuff are
// DB.Init ?
})
.then(() => {
// Someone needs routing?
})
...
.catch((e) => {
console.error("Ther app failed to start");
console.error(e);
});
I understand that in the last sample, you can not instantly query DB as it may not have connected yet, but this is a server, and users are usually expected to wait for your DB to init. However, if you wanna more proof solution, consider implementing something yourself in DB.submit to wait for the connect. Or, you can also use something like mongoose.