DEV Community

Cover image for How to create a simple and beautiful chat with MongoDB, Express, React and Node.js (MERN stack)

How to create a simple and beautiful chat with MongoDB, Express, React and Node.js (MERN stack)

Armel on December 30, 2019

Recently, I worked on an interesting project called SpeedBoard which is a real-time board for Agile and Scrum retrospectives. It's the kind of tool...
Collapse
 
syedabrar003 profile image
syedabrar003

I'm unable to deploy the master branch. I'm getting an error in the build.
npm ERR! code ENOENT
npm ERR! syscall open
npm ERR! path /tmp/build_4120397c_/client/package.json
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, open '/tmp/build_4120397c_/client/package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

   npm ERR! A complete log of this run can be found in:
   npm ERR!     /tmp/npmcache.VwQor/_logs/2020-08-04T20_56_07_801Z-debug.log
   npm ERR! code ELIFECYCLE
   npm ERR! errno 254
   npm ERR! chatfinal@1.0.0 postinstall: `npm install --prefix ./server && npm install --prefix ./client && npm run build --prefix ./client`
   npm ERR! Exit status 254
   npm ERR! 
   npm ERR! Failed at the chatfinal@1.0.0 postinstall script.
   npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

   npm ERR! A complete log of this run can be found in:
   npm ERR!     /tmp/npmcache.VwQor/_logs/2020-08-04T20_56_07_826Z-debug.log
Enter fullscreen mode Exit fullscreen mode

-----> Build failed

   We're sorry this build is failing! You can troubleshoot common issues here:
   https://devcenter.heroku.com/articles/troubleshooting-node-deploys

   Some possible problems:

   - Node version not specified in package.json
     https://devcenter.heroku.com/articles/nodejs-support#specifying-a-node-js-version

   Love,
   Heroku
Enter fullscreen mode Exit fullscreen mode

! Push rejected, failed to compile Node.js app.
! Push failed

Collapse
 
prachita11 profile image
Prachita Nayak

Hi , I faced the same issue , but in my case there was a .git folder present in my client folder which was preventing the deploy . just delete that .git folder and reinitialise your repository and the build will automatically start on heroku.

Collapse
 
nicodeio profile image
Nicolas Denoël • Edited

Hello, I have the same kind of issue first the same as syedabra003 but I added the node version to my package.json and then the error switched now to this one. I'm at the early step of the guide, when trying manual deploy with Heroku, I started from scratch two times but still the same result, 'npm run dev' runs fine on local so if any clues ? Thanks !

Collapse
 
armelpingault profile image
Armel

Hi, how do you reproduce the error exactly? Which command do you try to execute? Thanks

Collapse
 
syedabrar003 profile image
syedabrar003

Hey,
I followed every step in the post. This output is occurred when I entered 'npm run dev' in terminal.

Collapse
 
tonas_5 profile image
Drybone

In case anybody else runs into the same issue, I think the client's package "name" needs to be "client", in case you copy in your own react app... Also, Procfile should not have ' ' around the 'web: ... ' part. Not 100% if that's what solved it for me, but oh well.

Collapse
 
armelpingault profile image
Armel • Edited

Hi Drybone,

Yes, the Procfile should not include the single quote, I am using macOS and the command I wrote in the article is not adding the quote. Maybe it does under another OS? Anyway, the file should be like this:
github.com/armelpingault/speedchat...

And what do you mean exactly by the client's package "name" needs to be "client"?

I am not sure I understand :)

Collapse
 
prachita11 profile image
Prachita Nayak

yes removing the single quotes solved this issue for me.

Collapse
 
jatinranka profile image
JatinRanka

In server/Message.js file:

const messageSchema = new mongoose.Schema({
content: String,
name: String,
}, {
timestamps: true, -----> this line
});

Is timestamp true by default?

I tried _id.getTimestamp() on a MongoDB _id in which "timestamp: true" is not passed as a parameter, but is still returning the timestamp.

So, is it truly necessary or does it have any other use other than storing the time of creation?

Collapse
 
iamharsh profile image
Harsh Verma

timestamp : true, creates a createdAt and updatedAt field while your inserting documents in the document itself, by default it is not added.

The latter getTimestamp() is a function which finds created date. So there is a difference.

Collapse
 
jatinranka profile image
JatinRanka

I think you interpreted my doubt in the wrong way.

I didn't pass "timestamps: true" as a parameter. And still, it returned the createdAt field when I tried "_id.getTimestamp()". Then what is the use of passing "timestamps: true" as a parameter?

Thread Thread
 
iamharsh profile image
Harsh Verma • Edited

Lets take a sample model for a signup in mongoose -->
var userSchema = new Schema({
email : String,
password : String,
fullName : String,
userName : {
type : String,
unique : true
}
})

This piece of code will create a mongodb document of this format -->
{
"id" : ObjectId("5eac7f0101dce40f15a97e8d"),
"email" : "asd@asd.com",
"userName" : "hv98",
"fullName" : "asd",
"password" : "asd",
"
_v" : 0
}

Notice this doesn't have the createdAt and updatedAt fields

Now a sample model with the timestamp true field -->

var imageSchema = new Schema({
username : String,
description : String,
imagePath : {
type : String
},
comments : [commentSchema],
likes : [String],
nsfw : {
type : Boolean,
default : false
}

},{
timestamps : true
})

A document from this model would look like this -->
"id" : ObjectId("5eb02f999a15002d41f83e14"),
"likes" : [
"hv98"
],
"nsfw" : false,
"username" : "hv98",
"description" : "d",
"imagePath" : "1588604825052IMG_3265.JPG",
"comments" : [
{
"_id" : ObjectId("5eb1581ff810f83199fca925"),
"username" : "hv98",
"comment" : "dd",
"updatedAt" : ISODate("2020-05-05T12:12:15.736Z"),
"createdAt" : ISODate("2020-05-05T12:12:15.736Z")
}
],
"createdAt" : ISODate("2020-05-04T15:07:05.068Z"),
"updatedAt" : ISODate("2020-05-05T12:20:37.408Z"),
"
_v" : 0
}

Now if you notice this document has a field called createdAt and updatedAt which was not the case in the earlier one

So when you use _id.getTimestamp() you get the timestamp but it is not a field which is already present in the document but something which the function does and if you have the timestamp : true then this is a field in the document and doesn't require an extra function to be called.

I hope this can settle the difference.

Edit -- **
**One of the uses of the createdAt field is displaying the documents in ascending or descending order.

eg code -->
Image.find({}).sort({ createdAt: -1 }).exec(function(err, docs) {
if(err) console.log(err);
res.json(docs);
});

This returns all the documents and sort them in ascending order that is the latest doc is displayed first and sends it to your client.

Thread Thread
 
jatinranka profile image
JatinRanka • Edited

Amazing explanation Harsh. This cleared all my doubts.

Collapse
 
armelpingault profile image
Armel

Thanks for the reply Harsh ;)

Collapse
 
jeffsoriano profile image
Jeff Soriano

Can you please explain why you use socket.broadcast.emit for your 'push' event? It seems like socket.emit would work just fine but it doesn't. I've read this cheat sheet and it doesn't seem to explain why it wouldn't work:

socket.io/docs/v3/emit-cheatsheet/...

Collapse
 
armelpingault profile image
Armel

Hi Jeff, you might be right, I didn't test it with socket.emit, but it could be a mistake on my side ;)

Collapse
 
jeffsoriano profile image
Jeff Soriano • Edited

Thanks for the guide. I needed to install 'dotenv' and add require('dotenv').config(); to the top of index.js so that I could run it locally. I also needed to add this since I was using socket.io v3.0+:

const io = require('socket.io')(http, {
cors: {
origin: "localhost:3000",
methods: ["GET", "POST"]
}
});

To avoid the CORS errors

socket.io/docs/v3/migrating-from-2...

Collapse
 
yodist profile image
Yodi Satriani

Hi I have an issue to share with you guys. I got an issue if I wrap the with . You will have your code inside setState run twice. The possible way to fix this is to move the logic outside of setState. I have fix this two setState in App.js.

in componentDidMount -> I moved the msg.reverse() outside setState

componentDidMount() {
  this.socket = io(config[process.env.NODE_ENV].endpoint);

// Load the last 10 messages in the window.
this.socket.on('init', (msg) => {
  let msgReversed = msg.reverse();
  this.setState((state) => ({
    chat: [...state.chat, ...msgReversed],
  }), this.scrollToBottom);
});

// Update the chat if a new message is broadcasted.
  this.socket.on('push', (msg) => {
    this.setState((state) => ({
      chat: [...state.chat, msg],
    }), this.scrollToBottom);
  });
}

and in handleSubmit -> I moved the this.socket.emit function call outside. preventing from emit the message twice

handleSubmit(event) {
  // Prevent the form to reload the current page.
  event.preventDefault();

  // Send the new message to the server.
  this.socket.emit('message', {
    name: this.state.name,
    content: this.state.content,
  });

  this.setState((state) => {
    // Update the chat with the user's message and remove the current message.
    return {
      chat: [...state.chat, {
        name: state.name,
        content: state.content,
      }],
      content: '',
    };
  }, this.scrollToBottom);
}

Hope it can help. Thank you Armel.

Collapse
 
armelpingault profile image
Armel • Edited

Hi Yodi, thanks a lot, I have updated the source code on Github and in the article ;)

Collapse
 
sumnanazadi profile image
Sumnan Azadi

I tried this on my localhost but here I got
"GET http://localhost:5000/socket.io/?EIO=4&transport=polling&t=NMdKy4- net::ERR_FAILED"
"Access to XMLHttpRequest at 'http://localhost:5000/socket.io/?EIO=4&transport=polling&t=NMdKy4-' from origin 'localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."

Collapse
 
jeffsoriano profile image
Jeff Soriano

If you're using socket.io v3.0+ you need to add this to index.js in the server (replace old code with this):

const io = require('socket.io')(http, {
cors: {
origin: "localhost:3000",
methods: ["GET", "POST"]
}
});

socket.io/docs/v3/migrating-from-2...

Collapse
 
ananyaguptaa profile image
ananyaguptaa • Edited

How to create a simple and beautiful chat with MongoDB, Express, React and Node.js (MERN stack) is an interesting topic. Get some important points which I didn't know before. Thanks for sharing it. If you want to know about the MERN Stack course then visit: cetpainfotech.com/technolgy/mern-s...

Collapse
 
prachita11 profile image
Prachita Nayak

hey , mlab is discontinued and im completely new to heroku . which addon should be used now?

Collapse
 
armelpingault profile image
Armel

Hi Prachita, you can find a free development solution on mongodb.com/, this is what I am using right now ;)

Collapse
 
ananyaguptaa profile image
ananyaguptaa

After using that package.json file will be created I heard but I am unable to create if you an idea or suggestion where I could do a mistake

{
“name”: “test”,
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1"
},
“author”: “”,
“license”: “ISC”
}
CETPA- app directory is now set

Collapse
 
dineshigdd profile image
Daminda Dinesh W Imaduwa Gamage

Why do you need a Procfile in your file structure?
inside Procfile, web: node server/index.js.
Is it only for development? Does this file use when deploying the app?

Collapse
 
armelpingault profile image
Armel

Hi, it's used by Heroku: devcenter.heroku.com/articles/proc...

Collapse
 
siddm22 profile image
Siddhant Misra

Is there a reason you decided to let people enter a name? Anyone can type someone else's name and then type.

Collapse
 
armelpingault profile image
Armel

Hi Siddhant, well, like any other website, you can always use someone else name, and the point here is to show a tutorial about the MERN stack, not really about verifying the identity of a user. But you can always implement your own solution to verify your users' identity.

Collapse
 
seaspatton13 profile image
seaspatton13

changing the speechat app into a component. Is there a simple solution to change file structure? that way i can use the chat in a seperate app?

Collapse
 
armelpingault profile image
Armel

Yes, it depends exactly how you want to integrate it into your app. It's a bit hard to tell you exactly how to do it without an example though :)