Build Your Own Micro Framework and ORM with Node.js

Build Your Own Micro Framework and ORM with Node.js

Welcome to our latest newsletter! In this edition, we will walk you through creating your own micro-framework and ORM using Node.js. Whether you're looking to deepen your understanding of web development or create something custom-tailored to your needs, this tutorial has you covered.

Introduction

Creating your micro-framework can give you greater control over your projects, improve your understanding of underlying technologies, and provide a lightweight alternative to full-fledged frameworks like Express.js. We'll also include a simple ORM to interact with SQL databases.

Step 1: Setting Up Your Project

First, set up a new Node.js project and install the required packages:

Initialize the Project

mkdir my-micro-framework
cd my-micro-framework
npm init -y        

Install Dependencies

npm install sqlite3        


Step 2: Creating the Micro Framework

Let's create a basic structure for our micro framework, named Atomix. It will support REST API development with middleware, routing, and basic ORM functionality.

2.1 Creating the Core Framework

Create a file named atomix.js and start with the basic setup:

const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
const sqlite3 = require('sqlite3').verbose();

class Atomix {
    constructor() {
        this.routes = [];
        this.middlewares = [];
        this.templateRoot = path.join(__dirname, 'templates'); // Set template root
    }

    use(middleware) {
        this.middlewares.push(middleware);
    }

    route(method, path, handler) {
        this.routes.push({ method, path, handler });
    }

    async handler(req, res) {
        try {
            const parsedUrl = url.parse(req.url, true);
            req.pathname = parsedUrl.pathname;
            req.query = parsedUrl.query;
            req.body = await this.parseBody(req);

            for (const middleware of this.middlewares) {
                await middleware(req, res);
                if (res.finished) return;
            }

            const matchingRoute = this.routes.find(route => route.path === req.pathname && route.method === req.method);
            if (matchingRoute) {
                matchingRoute.handler(req, res);
            } else {
                res.statusCode = 404;
                res.end('404 Not Found');
            }
        } catch (err) {
            res.statusCode = 500;
            res.end('500 Internal Server Error');
        }
    }

    parseBody(req) {
        return new Promise((resolve, reject) => {
            let body = '';
            req.on('data', chunk => {
                body += chunk.toString();
            });
            req.on('end', () => {
                resolve(body ? JSON.parse(body) : {});
            });
        });
    }

    listen(port) {
        this.server = http.createServer((req, res) => this.handler(req, res));
        this.server.listen(port, () => {
            console.log(`Server running at https://localhost:${port}/`);
        });
    }

    render(res, templateName) {
        const filePath = path.join(this.templateRoot, templateName);
        fs.readFile(filePath, 'utf8', (err, data) => {
            if (err) {
                res.statusCode = 500;
                res.end('500 Internal Server Error');
                return;
            }
            res.statusCode = 200;
            res.setHeader('Content-Type', 'text/html');
            res.end(data);
        });
    }

    static(dir) {
        return (req, res, next) => {
            const filePath = path.join(dir, req.pathname);
            fs.readFile(filePath, (err, data) => {
                if (err) {
                    next();
                } else {
                    res.statusCode = 200;
                    res.end(data);
                }
            });
        };
    }
}
        


2.2 Adding a Simple ORM

Create a basic ORM to handle SQL-based databases. Add the following class to the atomix.js file:

class ORM {
    constructor(dbFile) {
        this.db = new sqlite3.Database(dbFile);
    }

    createTable(tableName, columns) {
        const cols = columns.map(col => `${col.name} ${col.type}`).join(', ');
        const query = `CREATE TABLE IF NOT EXISTS ${tableName} (${cols})`;
        return this.run(query);
    }

    insert(tableName, data) {
        const columns = Object.keys(data).join(', ');
        const placeholders = Object.keys(data).map(() => '?').join(', ');
        const query = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders})`;
        return this.run(query, Object.values(data));
    }

    get(tableName, conditions = {}) {
        const whereClause = Object.keys(conditions).map(key => `${key} = ?`).join(' AND ');
        const query = `SELECT * FROM ${tableName}${whereClause ? ' WHERE ' + whereClause : ''}`;
        return this.all(query, Object.values(conditions));
    }

    run(query, params = []) {
        return new Promise((resolve, reject) => {
            this.db.run(query, params, function (err) {
                if (err) {
                    reject(err);
                } else {
                    resolve(this);
                }
            });
        });
    }

    all(query, params = []) {
        return new Promise((resolve, reject) => {
            this.db.all(query, params, (err, rows) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(rows);
                }
            });
        });
    }
}
        


2.3 Using the Framework

Create a file named server.js to use the Atomix framework:

const Atomix = require('./atomix');
const orm = new Atomix.ORM('database.db');

const app = new Atomix();

// Middleware to log requests
app.use(async (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
});

// Serve static files from the 'public' directory
app.use(app.static('public'));

// Define a route for GET requests
app.route('GET', '/', (req, res) => {
    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify({ message: 'Welcome to the API!' }));
});

// Define a route for POST requests
app.route('POST', '/data', async (req, res) => {
    const data = req.body;
    await orm.insert('testTable', data);
    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify({ message: 'Data inserted', data }));
});

// Create a test table
orm.createTable('testTable', [
    { name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
    { name: 'name', type: 'TEXT' },
    { name: 'age', type: 'INTEGER' }
]).then(() => {
    // Start the server on port 3000
    app.listen(3000);
});
        

Step 3: Running Your Project

Create Necessary Directories

Create directories for static files and templates

mkdir public templates        

Create Sample HTML Files

  • Create an index.html file inside the templates directory.

Run the Server

node server.js        

You should see your server running on https://localhost:3000, with endpoints for handling GET and POST requests, logging middleware, and a simple ORM for database interactions.

Conclusion

Congratulations! You've built a micro framework and a basic ORM in Node.js. This tutorial demonstrates the power of understanding underlying technologies and creating custom solutions tailored to your needs. We hope this tutorial has been informative and inspiring.

Stay tuned for more tutorials and insights in our next newsletter!


If you found this tutorial helpful, please share it with your network and follow us for more tips and tricks in web development and software engineering.

Happy coding!


This is a comprehensive newsletter tutorial designed to guide readers through building their own micro-framework and ORM with Node.js. Feel free to customize and expand upon this framework to suit your specific needs.

要查看或添加评论,请登录

AtomixWeb Pvt. Ltd的更多文章

社区洞察

其他会员也浏览了