Express js crud tutorial with mongodb and ejs frontend

Express.js CRUD application with:

  • MongoDB (Mongoose) backend
  • Video & PDF upload
  • Course and Lecture model
  • EJS frontend templates


Open command prompt and create project folder by using following commands :-


mkdir  course-app


cd  course-app


and then type :-

npm init -y

and then  

Install dependencies


npm install express mongoose multer ejs dotenv method-override


then you will see your project folder like this :-




create config folder and create following file:-

File Upload Setup

multer.js  file code:-

const multer = require('multer');

const path = require('path');


const storage = multer.diskStorage({

  destination: function (req, file, cb) {

    cb(null, 'public/uploads/');

  },

  filename: function (req, file, cb) {

    cb(null, Date.now() + path.extname(file.originalname));

  }

});


module.exports = multer({ storage: storage });


and create .env file :-

MONGO_URI=mongodb://localhost:27017/courseApp


step 1: create models folder and inside it create two files 

Models

Course.js file:-


const mongoose = require('mongoose');


const CourseSchema = new mongoose.Schema({

  title: String,

  description: String,

});


module.exports = mongoose.model('Course', CourseSchema);


Lecture.js file:-


const mongoose = require('mongoose');

const LectureSchema = new mongoose.Schema({
  courseId: { type: mongoose.Schema.Types.ObjectId, ref: 'Course' },
  title: String,
  videoUrl: String,
  notesUrl: String,
});

module.exports = mongoose.model('Lecture', LectureSchema);


Step 2:-
create routes folder and inside it create courseRoutes.js  and lectureRoutes.js file :-

Routes
courseRoutes.js file code :-

const express = require('express');
const router = express.Router();
const Course = require('../models/Course');
const Lecture = require('../models/Lecture');

// List all courses
router.get('/', async (req, res) => {
  const courses = await Course.find();
  res.render('courses/index', { courses });
});

// New Course Form
router.get('/new', (req, res) => {
  res.render('courses/new');
});

// Create a new course
router.post('/', async (req, res) => {
  await Course.create(req.body);
  res.redirect('/courses');
});

// Edit Course Form (must come BEFORE '/:id')
router.get('/:id/edit', async (req, res) => {
  try {
    const course = await Course.findById(req.params.id);
    if (!course) return res.status(404).send('Course not found');
    res.render('courses/edit', { course });
  } catch (err) {
    console.error(err);
    res.status(500).send('Server Error');
  }
});

// Update Course
router.put('/:id', async (req, res) => {
  try {
    await Course.findByIdAndUpdate(req.params.id, req.body);
    res.redirect(`/courses/${req.params.id}`);
  } catch (err) {
    console.error(err);
    res.status(500).send('Update failed');
  }
});

// Delete Course (and its lectures)
router.delete('/:id', async (req, res) => {
  try {
    const courseId = req.params.id;
    await Lecture.deleteMany({ courseId }); // delete associated lectures
    await Course.findByIdAndDelete(courseId);
    res.redirect('/courses');
  } catch (err) {
    console.error(err);
    res.status(500).send('Delete failed');
  }
});

// Show course and its lectures
router.get('/:id', async (req, res) => {
  try {
    const course = await Course.findById(req.params.id);
    if (!course) return res.status(404).send('Course not found');
    const lectures = await Lecture.find({ courseId: course._id });
    res.render('courses/show', { course, lectures });
  } catch (err) {
    console.error(err);
    res.status(500).send('Error loading course');
  }
});

module.exports = router;


lectureRoutes.js file code :-


const express = require('express');
const router = express.Router();
const upload = require('../config/multer');
const Lecture = require('../models/Lecture');
const fs = require('fs');
const path = require('path');

// Show New Lecture Form
router.get('/new/:courseId', (req, res) => {
  res.render('lectures/new', { courseId: req.params.courseId });
});

// Create Lecture
router.post('/', upload.fields([{ name: 'video' }, { name: 'notes' }]), async (req, res) => {
  const { title, courseId } = req.body;
  const video = req.files['video']?.[0]?.filename || '';
  const notes = req.files['notes']?.[0]?.filename || '';

  await Lecture.create({
    title,
    courseId,
    videoUrl: '/uploads/' + video,
    notesUrl: '/uploads/' + notes,
  });

  res.redirect(`/courses/${courseId}`);
});

// Edit Lecture Form
router.get('/:id/edit', async (req, res) => {
  const lecture = await Lecture.findById(req.params.id);
  if (!lecture) return res.status(404).send('Lecture not found');
  res.render('lectures/edit', { lecture });
});

// Update Lecture
router.put('/:id', upload.fields([{ name: 'video' }, { name: 'notes' }]), async (req, res) => {
  const lecture = await Lecture.findById(req.params.id);
  if (!lecture) return res.status(404).send('Lecture not found');

  const { title } = req.body;

  if (req.files['video']) {
    if (lecture.videoUrl) fs.unlinkSync('public' + lecture.videoUrl);
    lecture.videoUrl = '/uploads/' + req.files['video'][0].filename;
  }

  if (req.files['notes']) {
    if (lecture.notesUrl) fs.unlinkSync('public' + lecture.notesUrl);
    lecture.notesUrl = '/uploads/' + req.files['notes'][0].filename;
  }

  lecture.title = title;
  await lecture.save();

  res.redirect(`/courses/${lecture.courseId}`);
});

// Delete Lecture
router.delete('/:id', async (req, res) => {
  const lecture = await Lecture.findById(req.params.id);
  if (!lecture) return res.status(404).send('Lecture not found');

  if (lecture.videoUrl) fs.unlinkSync('public' + lecture.videoUrl);
  if (lecture.notesUrl) fs.unlinkSync('public' + lecture.notesUrl);

  const courseId = lecture.courseId;
  await Lecture.findByIdAndDelete(req.params.id);

  res.redirect(`/courses/${courseId}`);
});

module.exports = router;




Step 3:-

Now create views folder and inside it create courses folder and inside courses folder create following files 


index.ejs:-


<h1>Courses</h1>

<a href="/courses/new" class="btn btn-primary mb-3">Add Course</a>

<ul class="list-group">
  <% courses.forEach(course => { %>
    <li class="list-group-item d-flex justify-content-between align-items-center">
      <div>
        <a href="/courses/<%= course._id %>" class="fw-bold text-decoration-none">
          <%= course.title %>
        </a>
      </div>
      <div>
        <a href="/courses/<%= course._id %>/edit" class="btn btn-sm btn-warning me-2">Edit</a>

        <form action="/courses/<%= course._id %>?_method=DELETE" method="POST" style="display:inline;">
          <button class="btn btn-sm btn-danger" onclick="return confirm('Delete this course?')">Delete</button>
        </form>
      </div>
    </li>
  <% }) %>
</ul>



new.ejs:-


<h1>New Course</h1>
<form action="/courses" method="POST">
  <input class="form-control" name="title" placeholder="Title"><br>
  <textarea class="form-control" name="description" placeholder="Description"></textarea><br>
  <button class="btn btn-success">Create</button>
</form>


edit.ejs:-



<h1>Edit Course</h1>

<form action="/courses/<%= course._id %>?_method=PUT" method="POST">
  <div class="mb-3">
    <label for="title" class="form-label">Course Title</label>
    <input type="text" class="form-control" name="title" id="title" value="<%= course.title %>" required>
  </div>

  <div class="mb-3">
    <label for="description" class="form-label">Description</label>
    <textarea class="form-control" name="description" id="description" rows="4"><%= course.description %></textarea>
  </div>

  <button class="btn btn-success">Update Course</button>
  <a href="/courses/<%= course._id %>" class="btn btn-secondary">Cancel</a>
</form>



show.ejs:-


<h1>Lectures for: <%= course.title %></h1>

<ul class="list-group">
  <% lectures.forEach(lec => { %>
    <li class="list-group-item">
      <div class="d-flex justify-content-between align-items-start">
        <div>
          <strong><%= lec.title %></strong><br>

          <% if (lec.videoUrl) { %>
            <video width="320" controls class="mt-2">
              <source src="<%= lec.videoUrl %>" type="video/mp4">
            </video><br>
          <% } %>

          <% if (lec.notesUrl) { %>
            <a href="<%= lec.notesUrl %>" target="_blank">Download Notes</a><br>
          <% } %>
        </div>

        <div>
          <a href="/lectures/<%= lec._id %>/edit" class="btn btn-sm btn-warning me-2">Edit</a>

          <form action="/lectures/<%= lec._id %>?_method=DELETE" method="POST" style="display:inline;">
            <button class="btn btn-sm btn-danger" onclick="return confirm('Delete this lecture?')">Delete</button>
          </form>
        </div>
      </div>
    </li>
  <% }) %>
</ul>


Step 4:-  create  lectures folder inside views folder and create following files

edit.ejs file :-

<h1>Edit Lecture</h1>
<form action="/lectures/<%= lecture._id %>?_method=PUT" method="POST" enctype="multipart/form-data">
  <input class="form-control" name="title" value="<%= lecture.title %>"><br>

  <label>Replace Video (optional):</label>
  <input class="form-control" type="file" name="video"><br>

  <label>Replace Notes (optional):</label>
  <input class="form-control" type="file" name="notes"><br>

  <button class="btn btn-success">Update Lecture</button>
</form>





new.ejs file:-

<h1>New Lecture</h1>
<form action="/lectures" method="POST" enctype="multipart/form-data">
  <input type="hidden" name="courseId" value="<%= courseId %>">
  <input class="form-control" name="title" placeholder="Lecture Title"><br>
  <label>Video:</label>
  <input class="form-control" type="file" name="video"><br>
  <label>PDF Notes:</label>
  <input class="form-control" type="file" name="notes"><br>
  <button class="btn btn-success">Upload</button>
</form>


app.js file code :-

// app.js
const express = require('express');
const mongoose = require('mongoose');
const methodOverride = require('method-override');
const path = require('path');
const dotenv = require('dotenv');
const expressLayouts = require('express-ejs-layouts'); 
const app = express();

// Load .env variables
dotenv.config();

// Connect to MongoDB
mongoose.connect(process.env.MONGO_URI || 'mongodb://localhost:27017/courseApp', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
})
.then(() => console.log('✅ MongoDB Connected'))
.catch((err) => console.error('MongoDB Connection Error:', err));

// Set view engine
app.set('view engine', 'ejs');


// Middlewares
app.use(express.urlencoded({ extended: true })); // Parse form data
app.use(methodOverride('_method'));              // Support PUT/DELETE in forms
app.use(express.static(path.join(__dirname, 'public'))); // Serve static files (like uploads, CSS)

// Routes
const courseRoutes = require('./routes/courseRoutes');
const lectureRoutes = require('./routes/lectureRoutes');

app.use('/courses', courseRoutes);
app.use('/lectures', lectureRoutes);

// Home route (redirect to courses)
app.get('/', (req, res) => {
  res.redirect('/courses');
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`);
});


How to Run Projects:-

open command prompt:-

cd  course-app

 and type following command to run projects:-

node app.js 







and click on course and add lectures .

and after adding open mongodb shell and check your databases :-






Comments

Popular posts from this blog

Express js Crud for adding courses and lectures with video upload and pdf notes with backend mongodb and front end in ejs

What is MongoDB & How to Install and use it