I'm working on a messaging application using Node.js, Express, MongoDB, and EJS. The goal is to retrieve chat messages from MongoDB and render them using EJS. However, I'm encountering an issue where allChats
is reported as undefined in my EJS template, even though there is data in the MongoDB collection. Below are the relevant parts of my code:
controllers/chat.js
):const Message = require("../models/chat.js");
const User = require("../models/user.js");
const axios = require("axios");
require("dotenv").config();
const accessToken = process.env.ACCESS_TOKEN; // Add your access token here
const myToken = process.env.VERIFY_TOKEN;
module.exports.getChats = async (req, res) => {
try {
const allChats = await Message.find({}).sort({ timestamp: 1 });
res.json(allChats);
res.render("./chats/showMessages.ejs", { allChats });
} catch (error) {
console.error("Error retrieving chats:", error);
res.sendStatus(500);
}
};
routes/chat.js
):const express = require("express");
const router = express.Router();
const chatController = require("../controllers/chat.js");
router.get("/chats", chatController.getChats);
router.post("/send", chatController.sendMessage);
router.post("/webhook", chatController.webhook);
router.get("/webhook", chatController.verifyWebhook);
module.exports = router;
views/showMessages.ejs
):<% layout("./layouts/boilerplate") %>
<div class="row conatiner p-2">
<div class="sidebar col-4 vh-100">
<form class="d-flex" role="search">
<input
class="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
<% for(let allChat of allChats) { %>
<div class="chatItem d-flex align-items-center">
<i class="fa-solid fa-user usrIcon mb-2 me-3"></i>
<div class="chatDetails mb-2 flex-grow-1">
<div class="chatHeader d-flex justify-content-between">
<span><%= allChat.from%></span>
<span>12:00</span>
</div>
<div class="chatMsg">
A snippet of a recent message sent by an user will show here...
</div>
</div>
</div>
<% } %>
</div>
<div class="chat-area col-8 vh-100">
<p>Chat messages will show here</p>
</div>
</div>
models/chat.js
):const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const chatSchema = new Schema(
{
from: {
type: Number,
},
to: {
type: Number,
},
message: {
type: String,
required: true, // Adding required to ensure messages are stored correctly
},
contact: {
type: Schema.Types.ObjectId,
ref: "User",
},
msg_is: String,
},
{ timestamps: true }
); // This will automatically add `created_at` and `updated_at` fields
const Chat = mongoose.model("Chat", chatSchema);
module.exports = Chat;
models/user.js
):const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema({
waId: { type: Number, unique: true },
name: String,
});
module.exports = mongoose.model("User", userSchema);
app.js
):const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const chatRoutes = require("./routes/chat.js");
const path = require("path");
const ejsMate = require("ejs-mate");
require("dotenv").config();
const app = express();
const port = process.env.PORT || 3000;
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
app.engine("ejs", ejsMate);
app.use(express.static(path.join(__dirname, "./public")));
// Connect to MongoDB
mongoose
.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to MongoDB");
})
.catch((error) => {
console.error("Error connecting to MongoDB:", error);
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.get("/", (req, res) => {
res.render("./chats/showMessages.ejs");
});
app.get("/chats/new", (req, res) => {
res.render("./chats/sendMessage.ejs");
});
app.use("/api", chatRoutes);
//working
app.listen(port, "0.0.0.0", () => {
console.log(`Server is listening on port ${port}`);
});
When I navigate to the root URL /
, which should render the showMessage.ejs
template with allChats
, I get an error saying allChats is not defined
. Despite having data in my MongoDB, it seems allChats
is not being passed correctly to the EJS template.
allChats
is populated (it shows the correct data).Message
collection has documents.getChats
.I expect to see a list of chat messages rendered in the showMessage.ejs
template.
The server throws an error stating allChats is not defined
when rendering the EJS template.
Can someone help me identify what might be going wrong? How can I ensure allChats
is correctly passed to the EJS template for rendering?
The problem is you're using the same template on /
and /chats
route, but on former you're not passing allChats
, hence the error.
A quick-and-dirty fix would be to check if the variable is defined in the template, like so (a better one would be to reorganize your code and use different templates, change logic etc.):
<% if(typeof allChats !== "undefined") { %>
<% for(let allChat of allChats) { %>
...
<% } %>
<% } %>
Next, remove res.json(allChats);
in chat
router, because you can end the response only once, res.render...
on the next line will throw headers already sent error.