Module 6: Working with Databases 💾


1. In-Memory Arrays වලින් ඔබ්බට

පසුගිය Module එකේදී අපි අපේ Notes API එකේ දත්ත ගබඩා කිරීමට in-memory array එකක් භාවිතා කළා. මෙය ඉගෙනීමේ අරමුණු සඳහා හොඳ වුවත්, සැබෑ ලෝකයේදී (production) එය කිසිසේත්ම ප්‍රායෝගික නැහැ. ඊට හේතුව, server එක restart කළ සැණින් array එකේ ඇති සියලුම දත්ත මැකී යාමයි. Application එකක දත්ත ස්ථිරවම (persistently) ගබඩා කර තබා ගැනීමට අපට **database** එකක් අවශ්‍ය වේ.

Database එකක් මගින් දත්ත ක්‍රමවත් ලෙස ගබඩා කිරීමට, පහසුවෙන් නැවත ලබා ගැනීමට, සහ ආරක්ෂාකාරීව කළමනාකරණය කිරීමට ඉඩ සලසයි.


2. SQL vs NoSQL: Database වර්ග දෙකක්

Databases ප්‍රධාන වශයෙන් වර්ග දෙකකට බෙදිය හැක:

SQL (Relational Databases)

මේවායේ දත්ත වගු (tables) වල පේළි (rows) සහ තීරු (columns) ලෙස ගබඩා කෙරේ. දත්ත අතර සබඳතා (relationships) පවත්වා ගැනීමට මේවා ඉතා සුදුසුය.

  • Schema: දැඩි, පූර්ව-නිශ්චිත ව්‍යුහයක් (strict schema) ඇත.
  • Query Language: SQL (Structured Query Language) භාවිතා කරයි.
  • උදාහරණ: MySQL, PostgreSQL, SQLite.

NoSQL (Non-Relational Databases)

මේවායේ දත්ත විවිධ ආකෘති වලින් ගබඩා කළ හැක. බහුලවම භාවිතා වන්නේ JSON වැනි document ආකෘතියයි. වේගයෙන් වෙනස් වන, විශාල දත්ත ප්‍රමාණයක් සඳහා මේවා වඩාත් සුදුසුය.

  • Schema: නම්‍යශීලී ව්‍යුහයක් (flexible/dynamic schema) ඇත.
  • Query Language: එක් එක් database එකට ආවේණික query methods ඇත.
  • උදාහරණ: MongoDB, CouchDB, Redis.

3. SQLite සමඟ CRUD Operations (SQL උදාහරණය)

SQLite යනු server එකක් අවශ්‍ය නොවන, file-based SQL database එකකි. කුඩා projects සහ ඉගෙනීම සඳහා මෙය ඉතාමත්ම සුදුසුය.

Project Setup

  1. පළමුව `sqlite3` package එක install කරගන්න:
  2. npm install sqlite3
  3. `database.js` නමින් file එකක් සාදා database connection එක සහ table එක නිර්මාණය කිරීමේ code එක ලියන්න.

`database.js` file එක:


const sqlite3 = require('sqlite3').verbose();

// 'notes.db' නමින් database file එකක් සාදා connection එකක් open කිරීම
const db = new sqlite3.Database('./notes.db', (err) => {
    if (err) {
        console.error(err.message);
    }
    console.log('Connected to the notes.db database.');
});

// 'notes' නමින් table එකක් නිර්මාණය කිරීම (දැනටමත් නොමැති නම්)
db.run(`CREATE TABLE IF NOT EXISTS notes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    content TEXT
)`, (err) => {
    if (err) {
        console.error(err.message);
    }
    console.log("'notes' table created or already exists.");
});

module.exports = db;
        

දැන් අපි අපේ Express app එකේ CRUD operations සඳහා මෙම database එක භාවිතා කරමු.

`index.js` file එක (SQLite සමඟ):


const express = require('express');
const app = express();
const port = 3000;
const db = require('./database.js'); // database connection එක import කිරීම

app.use(express.json());

// CREATE
app.post('/notes', (req, res) => {
    const { title, content } = req.body;
    const sql = `INSERT INTO notes (title, content) VALUES (?, ?)`;
    db.run(sql, [title, content], function(err) {
        if (err) {
            return res.status(400).json({ "error": err.message });
        }
        res.status(201).json({ "id": this.lastID });
    });
});

// READ (All)
app.get('/notes', (req, res) => {
    const sql = "SELECT * FROM notes";
    db.all(sql, [], (err, rows) => {
        if (err) {
            return res.status(400).json({ "error": err.message });
        }
        res.json({ "notes": rows });
    });
});

// UPDATE
app.put('/notes/:id', (req, res) => {
    const { title, content } = req.body;
    const sql = `UPDATE notes SET title = ?, content = ? WHERE id = ?`;
    db.run(sql, [title, content, req.params.id], function(err) {
        if (err) {
            return res.status(400).json({ "error": err.message });
        }
        res.json({ "message": "success", "changes": this.changes });
    });
});

// DELETE
app.delete('/notes/:id', (req, res) => {
    const sql = 'DELETE FROM notes WHERE id = ?';
    db.run(sql, req.params.id, function(err) {
        if (err) {
            return res.status(400).json({ "error": err.message });
        }
        res.json({ "message": "deleted", "changes": this.changes });
    });
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
});
        

මෙම code එක run කළ විට, `notes.db` නමින් file එකක් සෑදී, API endpoints හරහා කරන සියලුම වෙනස්කම් එම file එකේ ස්ථිරවම ගබඩා වේ.


4. ORMs / ODMs: Database සමඟ වැඩ කිරීම පහසු කරමු

ඔබට පෙනෙනවා ඇති SQL queries කෙලින්ම code එකේ ලිවීම තරමක් සංකීර්ණ සහ අපහසු විය හැකි බව. **ORM (Object-Relational Mapper)** සහ **ODM (Object-Document Mapper)** යනු මෙම ක්‍රියාවලිය පහසු කරන libraries වේ.

මේවා මගින් database tables හෝ collections, අපිට JavaScript වලදී හුරුපුරුදු objects සහ classes ලෙස හැසිරවීමට ඉඩ සලසයි. `db.run('SELECT * ...')` වැනි queries වෙනුවට `Note.find()` වැනි සරල functions භාවිතා කිරීමට අපට හැකිවේ.

5. Mongoose සමඟ CRUD Operations (NoSQL උදාහරණය)

Node.js ecosystem එකේ MongoDB සහ Mongoose සංයෝජනය ඉතාම ජනප්‍රියයි. අපි දැන් අපේ Notes API එක Mongoose භාවිතයෙන් නැවත ගොඩනගමු.

Project Setup

  1. `mongoose` package එක install කරගන්න:
  2. npm install mongoose
  3. (මේ සඳහා ඔබේ පරිගණකයේ හෝ MongoDB Atlas වැනි cloud සේවාවක් හරහා MongoDB database එකක් setup කර තිබිය යුතුය).
  4. Database connection එක සහ data model එක නිර්මාණය කරන්න.

`index.js` file එක (Mongoose සමඟ):


const express = require('express');
const mongoose = require('mongoose');

const app = express();
const port = 3000;

app.use(express.json());

// --- Database Connection ---
mongoose.connect('mongodb://localhost:27017/notesApp') // 'notesApp' is the database name
    .then(() => console.log('MongoDB connected...'))
    .catch(err => console.log(err));

// --- Mongoose Schema & Model ---
// Schema: දත්ත වල ව්‍යුහය (structure) නිර්වචනය කිරීම
const noteSchema = new mongoose.Schema({
    title: { type: String, required: true },
    content: String,
    createdAt: { type: Date, default: Date.now }
});

// Model: Schema එක භාවිතා කර database collection එක සමඟ සන්නිවේදනය කරන interface එක
const Note = mongoose.model('Note', noteSchema);

// --- API Routes using Mongoose ---

// CREATE
app.post('/notes', async (req, res) => {
    try {
        const newNote = new Note({
            title: req.body.title,
            content: req.body.content
        });
        const savedNote = await newNote.save();
        res.status(201).json(savedNote);
    } catch (err) {
        res.status(400).json({ message: err.message });
    }
});

// READ (All)
app.get('/notes', async (req, res) => {
    try {
        const notes = await Note.find();
        res.json(notes);
    } catch (err) {
        res.status(500).json({ message: err.message });
    }
});

// UPDATE
app.put('/notes/:id', async (req, res) => {
    try {
        const updatedNote = await Note.findByIdAndUpdate(
            req.params.id,
            { title: req.body.title, content: req.body.content },
            { new: true } // updated document එක return කිරීමට
        );
        res.json(updatedNote);
    } catch (err) {
        res.status(400).json({ message: err.message });
    }
});

// DELETE
app.delete('/notes/:id', async (req, res) => {
    try {
        await Note.findByIdAndDelete(req.params.id);
        res.json({ message: 'Note deleted successfully' });
    } catch (err) {
        res.status(500).json({ message: err.message });
    }
});

app.listen(port, () => {
    console.log(`Mongoose server running at http://localhost:${port}`);
});
        

Mongoose භාවිතයෙන් code එක කොතරම් පැහැදිලි සහ කියවීමට පහසු වී ඇත්දැයි බලන්න. SQL queries වෙනුවට `find()`, `save()`, `findByIdAndUpdate()` වැනි සරල, තේරුම්ගත හැකි methods භාවිතා කර ඇත.


Go Back to Module 5 Go to Module 7