This is how I've setup my expressjs server and my mongoDB (mongoDB native driver, not mongoose). Now I would like to check for some existing documents in the DB to add some fixture documents on start of the server.
I do not understand where to do that.
Something like:
const hasAdmin = db.collection('users').findOne({ username: 'admin' })
if (!hasAdmin) {
// Add some data to collection
}
app.js
const app = express()
const mongoDB = mongodb://localhost:27017/mydb
// Parse application/json
app.use(bodyParser.json())
// Parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({
extended: false
}))
// GraphQL
app.use('/graphql',
bodyParser.text({ type: 'application/graphql' }),
graphqlMiddleware,
graphqlExpress(req => ({
schema: schema
}))
)
// Connect to Mongo on start
db.connect(mongodb, (err) => {
if (err) {
console.error('Unable to connect to Mongo: ' + err)
process.exit(1)
} else {
app.listen(port, () => {
console.log('Listening on port ' + port)
})
}
})
module.exports = app
You can in theory put it anywhere there is a Mongo connection open.
I would recommend to extract your database-related functionality into a separate class (and a separate file for that; separation of concerns).
You could then call the functionality that fills your database initially from the constructor of that class.
Something like this (this is not working code, but should give you an idea):
db-helper.js
class DbHelper {
constructor() {
this.connect();
}
connect() {
// establish mongo connection
mongo.connect("...", (err, db) => {
this.db = db;
// then take care of initial filling
this.ensureDatabaseIsFilled();
});
}
ensureDatabaseIsFilled() {
const hasAdmin = this.db.collection('users').findOne({ username: 'admin' })
if (!hasAdmin) {
// Add some data to collection
}
}
/**
* Random method that retrieves data from mongo
*/
getRandomThing() {
return new Promise((resolve, reject) => {
this.db.find({...}, (err, result) => resolve(result));
});
}
}
module.exports = DbHelper;
app.js
const DbHelper = require('./db-helper');
const dbHelper = new DbHelper();
// ....
app.get('/example', (req, res) => {
dbHelper.getRandomThing().then(result => res.send(result));
});
// ....
Related
What I want to do is to use different databases for different users, for example I have 3 users that would connect to:
www.user1.myfrontend.com
www.user2.myfrontend.com
www.user3.myfrontend.com
Let's suppose that each user want to display the list of products he has, a GET request would be sent to the backend and from there I will connect to the database of the user:
mongodb://mongodatabse:secret#server/databaseOfUser1
mongodb://mongodatabse:secret#server/databaseOfUser2
mongodb://mongodatabse:secret#server/databaseOfUser3
What I did so far:
I connect to the database called config at the start of the app:
db.js
const connect = (uri, app, database="config") => {
const db= mongoose
.createConnection(uri,{ useNewUrlParser: true, useUnifiedTopology: true })
db.on('open', () => {
mongoose.connection.useDb("config")
app.emit('dbReady');
return true;
});
db.on('error', (err) => {
console.log(`Couldn't connect to database': ${err.message}`);
return false;
});
};
server.js
db.connect(process.env.MONGODB_URL, app);
app.on('dbReady', function () {
server.listen(PORT, () => {
console.info(`> Frontend is hosted #: ${process.env.BASE_URL}`);
console.info(`> Database is hosted #: ${process.env.mongodb_url}`);
console.info(`> Listening on port ${PORT}`);
});
});
Then whenever I receive a request I check in the config database for the database to use:
app.js:
const AccessConfig = require('./models/schemas/AccessConfigSchema');
const db = require('./models/db');
app.use(async (req, res, next) => {
const subdomain = req.subdomains[req.subdomains.length - 1];
try {
let database = await AccessConfig.findOne({ subdomain: subdomain});
if (!database)
database= await AccessConfig.findOne({ subdomain: "demo"});
console.log(database);
db.useDb(database);
next();
} catch (e) {
console.log(e.message)
return res.status(500).send('Error: ' + e.message);
}
});
So far It seems like the database isn't changing and I'm not even sure that this is the correct implementation or too many connections are open, etc.
I figured out, you can create connections using
//DB_URI_USER1=mongodb://mongodatabse:secret#server/databaseOfUser1
//DB_URI_USER2=mongodb://mongodatabse:secret#server/databaseOfUser2
const user1Connection = mongoose.createConnection(process.env.DB_URI_USER1, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
const user2Connection = mongoose.createConnection(process.env.DB_URI_USER2, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
Then you create the model for each
const User1 = user1Connection.model(...)
const User2 = user2Connection.model(...)
Now on the API you query the desired model.
Working for me :)
I'm using NodeJS w/ Express to create a web app that records your audio using the VMSG library and posts the BLOB audio to my file system using HTTP Requests and multer. It also adds that instance of a recording into a MongoDB database.
I'm having an issue with the fetch command. It's not working unless I put an alert right after the fetch. The way I have it set up is that I have my main express app (index.js), and a router to the /recordingsDirectory (recordings.js) which is the endpoint for processing the posts. My main index HTML page uses Handlebars and uses a separate JS script (recorder.js) to 1) use the VMSG library and 2) fetch a POST to the /recordingsDirectory once someone submits the audio file w/ the name and the AudioBlob present. This is where I'm stuck. I can fetch in recorder.js with an alert line after the fetch, but I can't have the fetch on the end of the else if block by itself. I'd like to do it without this since the alert is ugly. A solution I've tried is that I tried to make the onsubmit function async and await fetch since I thought maybe it's waiting for a promise but that didn't work.
Here are the files. I commented CRITICAL and SUPER CRITICAL to the lines of code that you should check out and I think where the issues lie:
index.js
const express = require('express')
const handlebars = require('express-handlebars')
const path = require('path')
const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
const xhr = new XMLHttpRequest()
const db = require('./db')
const app = express()
const PORT = process.env.PORT || 8000
app.set('view engine', 'hbs')
app.engine('hbs', handlebars({
layoutsDir: path.join(__dirname, 'views', 'layouts'),
extname: 'hbs',
defaultLayout: 'index',
partialsDir: path.join(__dirname, 'views', 'partials'),
}))
app.use(express.json())
app.use(express.urlencoded({extended: false}))
app.use((err, req, res, next) => {
if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
return res.status(400).send({ status: 404, message: err.message })
}
next()
})
app.get('/', (req, res) => {
res.render('main', {
title: 'Main Page'
})
})
app.get('/recordings', (req, res) => {
var database = db.get().db('AudioJungle')
database.collection('recordings').find().sort({ "date": -1 }).toArray(function(err, docs) {
res.render('recordings', {
title: 'Recordings',
recordings: docs
})
})
})
// CRITICAL
app.use('/recordingsDirectory', require('./recordings/recordings'))
app.use(express.static('public'))
app.use('/scripts', express.static(path.join(__dirname, 'node_modules', 'vmsg')))
db.connect(function(err) {
if (err) {
console.log('Unable to connect to Mongo.')
process.exit(1)
} else {
app.listen(PORT, () => console.log(`Listening on Port: ${PORT}`))
}
})
process.on('SIGINT', function() {
db.close(function () {
console.log('Disconnected on app termination');
process.exit(0);
});
});
app.use((req, res, next) => {
res.status(404).send({
status: 404,
error: 'Not found'
})
})
recordings.js (Aka the /recordingsDirectory endpoint for a fetch POST)
const express = require('express')
const router = express.Router()
const multer = require('multer')
const fs = require('fs-extra')
const db = require('../db')
const { ObjectId } = require('bson')
const moment = require('moment')
const upload = multer({
storage: multer.diskStorage({
destination: (req, file, callback) => {
let path = './public/uploads'
fs.mkdirsSync(path)
callback(null, path)
},
filename: (req, file, callback) => {
createRecording(req).then((id) => {
var file_name = id + '.mp3'
callback(null, file_name)
})
}
})
})
var type = upload.single('audio-file')
// CRITICAL
router.post('/', type, (req, res) => {
console.log('made it')
res.status(200)
res.send('OK')
})
router.delete('/delete', (req, res) => {
deleteRecording(req.body._id).then((dbResponse) => {
if (dbResponse == null || dbResponse == undefined) {
res.status(400).json({ msg: 'ID already deleted' })
} else {
res.status(200)
}
})
})
router.get('/', (req, res) => {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
recordings.findOne({"_id": ObjectId(req.query.id)}, function(err, result) {
if (err) throw err
if (result == null || result == undefined) {
return res.status(400).json({
status: 404,
error: 'Recording no longer in the database'
})
}
res.status(200)
res.json({
name: result.name,
date: result.date
})
})
})
async function createRecording(req) {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
var audioObject = {
name: req.body.name,
date: moment().format('MMMM Do YYYY, h:mm:ss a')
}
var dbResponse = await recordings.insertOne(audioObject)
return dbResponse.insertedId
}
async function deleteRecording(id) {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
var audioToDelete = {
_id: ObjectId(id)
}
var deleteResult = await recordings.deleteOne(audioToDelete)
return deleteResult
}
module.exports = router
And below is the Script the audio and name and tries to Fetch (where I need the alert for it to actually process into the /recordingsdirectory)
recorder.js
import { record } from "/scripts/vmsg.js";
let recordButton = document.getElementById("record");
var blobObj = null
recordButton.onclick = function() {
record({wasmURL: "/scripts/vmsg.wasm"}).then(blob => {
blobObj = blob
var tag = document.createElement("p")
tag.id="finishedRecording"
var text = document.createTextNode("Audio File Recorded")
tag.appendChild(text)
var element = document.getElementById("box")
element.appendChild(tag)
document.getElementById('box').appendChild(a)
})
}
let form = document.getElementById('mp3Form');
form.addEventListener("submit", submitAudio)
function submitAudio() {
var fileName = form.elements[0].value
if (fileName == "") {
alert('Please enter a name for your file')
} else if (blobObj != null) {
// CRITICAL
// SUPER CRITICAL WHERE FETCH DOESN'T WORK UNLESS I PUT AN ALERT AT THE END
const formData = new FormData()
formData.append('name', fileName)
formData.append('audio-file', blobObj)
const options = {
method: 'POST',
body: formData
}
fetch('/recordingsDirectory', options);
// If I comment an alert here, /recordingsDirectory will process the post since it console.logs 'made it'
} else {
alert('Record some Audio to upload')
}
}
Here's my file system.
Also, I'd like to mention that the fetch works properly on my Windows PC without having to add the alert, but it doesn't work without the alert on my macbook. If any one figures out a fix or an error in how I'm doing things to allow this please let me know. I've been stuck on this problem for a day now. Thanks a bunch!
I have a list of articles that have a property views and I want to increment that property in the database each time a user clicks on an article title. Currently nothing happens when I do it. Why isn't it working and how can I increment that property each time on click? Here is my React part:
const incrementViews = (id) => {
var item = posts.find(x => x._id === id);
item.views += 1;
}
<div className="post-title">
<Link to={`/post/${post._id}`}>
<h2><a href="#" onClick={() => incrementViews(post._id)}>{post.title}</a>
</h2>
</Link>
</div>
and my server.js:
// Requiring the dependencies
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
require('dotenv').config();
const mongoose = require('mongoose');
const PORT = process.env.PORT || 3001;
const BASE_URL = process.env.REACT_APP_BASE_URL;
console.log(BASE_URL)
const itemRoutes = express.Router();
let Comment = require('./comment.model');
app.use(cors());
app.use(bodyParser.json());
mongoose.connect(BASE_URL, { useNewUrlParser: true })
const connection = mongoose.connection;
connection.once('open', function () {
console.log('Connection to MongoDB established succesfully!');
});
let collection = connection.collection("posts_with_tags_test");
collection.createIndex(
{
postContent: 'text',
title: 'text'
}
);
// Serve static assets
if (process.env.NODE_ENV === 'production') {
app.use(express.static('build'));
}
itemRoutes.route('/').get(async (req, res) => {
let collection = connection.collection("posts_with_tags_test");
let response = await collection.find({})
.toArray();
res.send(response);
});
itemRoutes.route('/search').post(async (req, res) => {
let result = await connection.collection("posts_with_tags_test").find({
$text: {
$search: req.body.searchString
}
}).toArray();
res.send(result);
});
itemRoutes.route("increment/:id"").post(async (req, res) => {
const { id } = req.params;
collection.updateOne({ _id: id }, { $inc: { views: 1 } });
return res.status(200).json({ msg: "OK" });
});
itemRoutes.route('/comments').get(async (req, res) => {
let collection = connection.collection("comments");
let response = await collection.find({})
.toArray();
res.send(response);
});
itemRoutes.route('/comments')
.post((req, res) => {
res.setHeader('Content-Type', 'application/json');
let comment = new Comment(req.body);
comment.save()
.then(comment => {
res.status(200).json({ comment })
})
.catch(err => {
res.status(400).send('failed')
})
});
app.use('/', itemRoutes);
app.use('/comments', itemRoutes);
app.use('/search', itemRoutes);
app.use('/increment', itemRoutes);
app.listen(PORT, function () {
console.log('Server is running on' + ' ' + PORT);
})
I think there are two problems in frontend and backend respectively.
Front-end
You should use post variable as a state variable so as to re-render then component when changes are made on post.
Back-end
There is no issue with increasing view in your code.
Here, you need to return success status.
The function incrementViews only increments views on the frontend and never sends any data to the API. One way you can make it work is as follows:
server.js
itemRoutes.route("/increment/:id").post(async (req, res) => {
const { id } = req.params;
collection.updateOne({ _id: id }, { $inc: { views: 1 } });
return res.status(200).json({ msg: "OK" });
});
React
const incrementViews = (id) => {
// Assuming your API server is running on port 5000.
fetch(`http://localhost:5000/increment/${id}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
})
.then((res) => res.json())
.then(console.log)
.catch(console.error);
};
Update
The reason you're getting 404 is a missing colon : in the route parameters.
// Notice the :id, colon is important.
itemRoutes.route("/increment/:id").post(async (req, res) => {
const { id } = req.params;
// ...
});
Here is a demo reproduced on Glitch. Removed database logic and just added a response messages.
I tested the demo using Postman and it works fine.
On a POST request to https://adaptive-sassy-legal.glitch.me/increment/123, should return a response as shown below.
{
msg: "itemsRoute increment.",
id: "123"
}
Update 2
Another thing which I forgot to mention in the previous update was to update the middleware.
// Use only `/increment` instead of `increment/:id`.
app.use("/increment", itemRoutes);
Here is an updated demo.
I am designing a middleware between the client and the GraphQL API. The idea is to create a route that all client requests go to first /filament. This route then decides whether it can send data back from the cache or to proceed on to /graphql to access the resolvers there to get data from the database. I am using Axios to make my requests but it isn't firing server side. I wanted to ask if this was just an improper way to use Axios or if I am doing it incorrectly.
Server.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const fetch = require("node-fetch");
const redis = require("redis");
const PORT = process.env.PORT || 4000;
// const REDIS_PORT = process.env.REDIS_PORT || 6379;
//Create Redis client on Redis port (optional)
const client = redis.createClient();
const schema = require('./schema');
const bluebird = require('bluebird')
const app = express()
// bluebird.promisifyAll(redis.RedisClient.prototype);
// bluebird.promisifyAll(redis.Multi.prototype);
app.use(express.json())
client.on("error", err => {
console.log("Error " + err);
});
client.on('connect', () => {
console.log('Redis client connected');
});
// pass redis as context so our schema can use Redis methods
const wrapper = require('./filamentMiddleware')
app.use('/filament',
wrapper(client), // filamentMiddleware with access to client
)
app.use(
'/graphql',
graphqlHTTP((req) => ({
schema,
graphiql: true,
context: {
client,
req: req
}
})),
// addToCacheWrapper(res.data)
);
app.listen(PORT, () =>
console.log(`GraphQL server is running on port: ${PORT}`)
);
Middleware
const axios = require('axios')
const serverFilamentQuery = require('./serverFilamentQuery')
const mergeDataFromCacheAndServer = require('./mergeDataFromCacheAndServer')
const wrapper = (client) => (req, res, next) => {
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
client.get(ip, (err, cacheData) => {
const { query } = req.body
// ip not found in cache
if (!cacheData) {
console.log('cacheData:', cacheData)
axios.post('/graphql', { query }).then(res => {
console.log('have we made the post ????')
// set redis cache
// set the new data in Redis
client.set(ip, JSON.stringify({
todos: res.data.data['todos']
}), (err, result) => {
console.log(result)
})
const { data } = res.data
// return combinedData to client
return res.status(200).json({ data })
})
}
// console.log('ip found in cache')
const [newQuery, data, isMatched] = serverFilamentQuery(query, cacheData)
if (isMatched) {
return res.status(200).json({ data })
} else {
axios.post('/graphql', { newquery }).then(res => {
const merged = mergeDataFromCacheAndServer(data['todos'], res.data.data['todos']);
// set the new data in Redis
console.log(merged)
client.set(ip, JSON.stringify({
todos: merged
}), (err, result) => {
console.log(result)
})
// return combinedData to client
return res.status(200).json({ merged })
})
}
})
}
module.exports = wrapper
Yes, there is no problem making a post request server-side with Axios.
As per the Axios GitHub page
Promise based HTTP client for the browser and node.js (ie. server-side)
Note that the server-side call/usage is slightly different though, as per the docs here
versus client-side call here
I'm trying to learn how to use the MERN stack. I've been following a YouTube tutorial (https://www.youtube.com/watch?v=WT67-OETeGU). Currently, I've defined and created my server.js file as follows:
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
const todoRoutes = express.Router();
const PORT = 4000;
let Todo = require("./todo.model.js");
app.use(cors());
app.use(bodyParser.json());
mongoose.connect("mongodb://127.0.0.1:27017/todos", {
useUnifiedTopology: true,
useNewUrlParser: true
});
const connection = mongoose.connection;
todoRoutes.route("/").get(function(req, res) {
Todo.find(function(err, todos) {
if (err) {
console.log("error getting data");
} else {
res.json(todos);
}
});
});
todoRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Todo.findById(id, function(err, todo) {
if (err) {
console.log("error finding todo object with id: " + id);
} else {
res.json(todo);
}
});
});
todoRoutes.route("/add").post(function(req, res) {
let todo = new Todo(req.body);
todo
.save()
.then(todo => {
res.status(200).json({ todo: "Todo added successfully" });
})
.catch(err => {
res.status(400).send("failed to save new todo");
});
});
todoRoutes.route("/update/:id").post(function(req, res) {
let id = req.params.id;
Todo.findById(id, function(err, todo) {
if (!todo) {
res.status(400).send("cant update id: " + id);
} else {
todo.todo_description = req.body.todo_description;
todo.todo_responsible = req.body.todo_responsible;
todo.todo_priority = req.body.todo_priority;
todo.todo_completed = req.body.todo_completed;
todo
.save()
.then(todo => {
res.json("Todo updated");
})
.catch(err => res.status(400).send("update not possible"));
}
});
});
app.use("./todos", todoRoutes);
I am trying to test my API out, so I installed Postman and set it up. I'm trying to make a GET request to: http://localhost:4000/todos. However, Postman only returns a 404, saying "Could not get any response".
I've run the commands mongod and mongo on my terminal to get Mongo running.
Any suggestions on where I've gone wrong?
It seems you are setting your endpoints as relative paths. Can you remove the dot proceeding "/todos"? in app.use("./todos", todoRoutes);
`