Module 9: Project: Notes API Backend 🛠️


1. Project Goal: අපි හදන්නේ මොකක්ද?

මෙම project එකේ අරමුණ වන්නේ, පසුගිය modules වලින් අප ඉගෙන ගත් සියලුම දේ (Express, MongoDB, Mongoose, JWT Authentication, Error Handling, Validation) ප්‍රායෝගිකව යොදාගෙන, සම්පූර්ණ backend API එකක් මුල සිට ගොඩනැගීමයි.

අප නිර්මාණය කරන Notes API එකේ ප්‍රධාන features:

2. Project Structure: Code එක සංවිධානය කිරීම

හොඳ project එකකදී code එක ක්‍රමවත්ව සංවිධානය කිරීම ඉතා වැදගත් වේ. අපි පහත folder structure එක භාවිතා කරමු:

/notes-api
|-- /config
|   |-- db.js             # Database connection logic
|-- /middleware
|   |-- auth.js           # JWT authentication middleware
|   |-- errorHandler.js   # Custom error handler
|-- /models
|   |-- Note.js           # Note Mongoose model
|   |-- User.js           # User Mongoose model
|-- /routes
|   |-- auth.js           # Authentication routes (register, login)
|   |-- notes.js          # CRUD routes for notes
|-- .env                  # Environment variables (DB URI, JWT Secret)
|-- .gitignore            # Git ignore file
|-- package.json
|-- server.js             # Main server entry point
        

3. Project Setup: පියවරෙන් පියවර

Step 1: Initialize Project and Install Dependencies

Terminal එකේ පහත commands run කරන්න:


mkdir notes-api
cd notes-api
npm init -y
npm install express mongoose bcryptjs jsonwebtoken dotenv joi
npm install --save-dev nodemon
        

ඔබේ package.json file එකේ, "scripts" කොටසට "start": "node server.js" සහ "dev": "nodemon server.js" ලෙස එකතු කරගන්න.

Step 2: Create Folder Structure

ඉහත පෙන්වා ඇති පරිදි config, middleware, models, සහ routes folders සාදාගන්න.

Step 3: Setup Environment Variables

Project එකේ root එකේ .env නමින් file එකක් සාදා, ඔබේ MongoDB connection string එක සහ JWT secret key එක ඇතුළත් කරන්න.

.env file එක:


MONGO_URI=mongodb://localhost:27017/notesApiProject
JWT_SECRET=thisisareallylongandsecuresecretkey
PORT=5000
        

⚠️ වැදගත්: .gitignore file එකක් සාදා, එහි node_modules සහ .env යන දෙකම ඇතුළත් කරන්න. .env file එකේ ඇති රහස්‍ය තොරතුරු කිසිවිටෙකත් Git repository එකකට commit නොකළ යුතුය.


4. The Code: Files එකින් එක ගොඩනගමු

1. Config: Database Connection (config/db.js)


const mongoose = require('mongoose');

const connectDB = async () => {
    try {
        await mongoose.connect(process.env.MONGO_URI);
        console.log('MongoDB Connected...');
    } catch (err) {
        console.error(err.message);
        process.exit(1); // Exit process with failure
    }
};

module.exports = connectDB;
        

2. Models: User and Note (models/User.js & models/Note.js)

User model එක අපි 7 වන module එකේදී සාදන ලදී. Note model එකට, එය කුමන user ට අයිතිද යන්න track කිරීමට `user` field එකක් එකතු කරමු.

models/Note.js file එක:


const mongoose = require('mongoose');

const NoteSchema = new mongoose.Schema({
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User', // 'User' model එකට සම්බන්ධ කිරීම
        required: true,
    },
    title: {
        type: String,
        required: true,
        trim: true,
    },
    content: {
        type: String,
        required: true,
    },
    createdAt: {
        type: Date,
        default: Date.now,
    },
});

module.exports = mongoose.model('Note', NoteSchema);
        

3. Middleware: Auth & Error Handling

Module 7 සහ 8 හි නිර්මාණය කළ middleware/auth.js සහ middleware/errorHandler.js files ඒ ආකාරයෙන්ම භාවිතා කළ හැක.

4. Routes: Auth & Notes

routes/auth.js file එක:

Module 7 හි JWT සහ bcrypt සමඟ නිර්මාණය කළ registration සහ login routes මෙහි ඇතුළත් කරන්න.

routes/notes.js file එක:

මෙය අපේ project එකේ ප්‍රධානතම කොටසයි. මෙහිදී අපි note එකක් create, read, update, සහ delete කිරීමේදී, `auth` middleware එක යොදාගෙන, login වූ user ට පමණක් එම ක්‍රියාවන් කිරීමට ඉඩ ලබා දෙනවා.


const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const Note = require('../models/Note');
const Joi = require('joi');

// Note validation schema
const noteSchema = Joi.object({
    title: Joi.string().min(3).required(),
    content: Joi.string().min(5).required(),
});

// @route   POST api/notes
// @desc    Create a new note
// @access  Private
router.post('/', auth, async (req, res, next) => {
    try {
        await noteSchema.validateAsync(req.body);
        const { title, content } = req.body;
        const newNote = new Note({
            user: req.user.id,
            title,
            content,
        });
        const note = await newNote.save();
        res.status(201).json(note);
    } catch (error) {
        if (error.isJoi) return res.status(400).json({ message: error.details[0].message });
        next(error);
    }
});

// @route   GET api/notes
// @desc    Get all notes for a user
// @access  Private
router.get('/', auth, async (req, res, next) => {
    try {
        const notes = await Note.find({ user: req.user.id }).sort({ createdAt: -1 });
        res.json(notes);
    } catch (err) {
        next(err);
    }
});

// @route   PUT api/notes/:id
// @desc    Update a note
// @access  Private
router.put('/:id', auth, async (req, res, next) => {
    try {
        await noteSchema.validateAsync(req.body);
        let note = await Note.findById(req.params.id);
        if (!note) return res.status(404).json({ message: 'Note not found' });
        // User තමන්ගේම note එකක් update කරනවාදැයි පරීක්ෂා කිරීම
        if (note.user.toString() !== req.user.id) {
            return res.status(401).json({ message: 'Not authorized' });
        }
        note = await Note.findByIdAndUpdate(req.params.id, { $set: req.body }, { new: true });
        res.json(note);
    } catch (error) {
        if (error.isJoi) return res.status(400).json({ message: error.details[0].message });
        next(error);
    }
});

// @route   DELETE api/notes/:id
// @desc    Delete a note
// @access  Private
router.delete('/:id', auth, async (req, res, next) => {
    try {
        let note = await Note.findById(req.params.id);
        if (!note) return res.status(404).json({ message: 'Note not found' });
        if (note.user.toString() !== req.user.id) {
            return res.status(401).json({ message: 'Not authorized' });
        }
        await Note.findByIdAndDelete(req.params.id);
        res.json({ message: 'Note removed' });
    } catch (err) {
        next(err);
    }
});

module.exports = router;
        

5. Main Server File (server.js)

අවසාන වශයෙන්, අපි මේ සියලු කොටස් එකට සම්බන්ධ කර server එක run කරමු.


const express = require('express');
const dotenv = require('dotenv');
const connectDB = require('./config/db');
const errorHandler = require('./middleware/errorHandler');

// Load env vars
dotenv.config();

// Connect to database
connectDB();

const app = express();

// Body Parser Middleware
app.use(express.json());

// Mount routers
app.use('/api/auth', require('./routes/auth'));
app.use('/api/notes', require('./routes/notes'));

// Error Handler Middleware (must be last)
app.use(errorHandler);

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
        

5. API එක Test කිරීම

Postman වැනි API client එකක් භාවිතා කර, ඔබේ endpoints test කරන්න.

  1. Register: `POST` request එකක් `/api/auth/register` වෙත යවා user කෙනෙක් සාදන්න.
  2. Login: `POST` request එකක් `/api/auth/login` වෙත යවා, response එකේ එන JWT token එක copy කරගන්න.
  3. Create Note: `POST` request එකක් `/api/notes` වෙත යවන්න. Headers වල, `x-auth-token` නමින් key එකක් සාදා, එහි value එක ලෙස copy කරගත් token එක paste කරන්න. Body එකේ note එකේ title සහ content යවන්න.
  4. Get Notes: Token එක සමඟ `GET` request එකක් `/api/notes` වෙත යවන්න.

Go Back to Module 8 Go to Module 10