DEV Community

Albert
Albert

Posted on • Updated on

Express JS: Handling endpoints dynamically with classes

// main.js

import { registerEndpoints } './utils/registry'

const express = require("express");

const Application = express();

(async () => {
    await registerEndpoints(Application, "../routes");
})();
Enter fullscreen mode Exit fullscreen mode
// src/utils/registry 

export async function registerEndpoints(
    application: Application,
    dir: string = ""
) {
    const filePath = path.join(__dirname, dir);
    const files = await fs.readdir(filePath);
    const originalDir = dir;
    for (const file of files) {
        const stat = await fs.lstat(path.join(filePath, file));
        if (stat.isDirectory())
            registerEndpoints(application, path.join(dir, file));
        if (file.endsWith(".js") || file.endsWith(".ts")) {
            const { default: Endpoint } = await import(path.join(dir, file));
            const endpoint = new Endpoint();

            application.use(endpoint.path, endpoint.run);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
// src/utils/structures/BaseRoute

export default abstract class BaseRoute {
    constructor(private path: string) {}

    getPath(): string {
        return this.path;
    }

    abstract run(req, res, next): void;
}
Enter fullscreen mode Exit fullscreen mode
// src/routes/mySingleCallbackRoute.ts

import BaseRoute from "src/utils/structures/BaseRoute";

export default class mySingleCallbackRoute extends BaseRoute {
    constructor() {
        super("/mySingleCallbackRoute");
    }

    async run(req, res) {
            res.send("Hello!")
    }
}
Enter fullscreen mode Exit fullscreen mode
// src/routes/myMultipleCallbacksRoute.ts

import BaseRoute from "src/utils/structures/BaseRoute";

function Multiple(...functions) {
    return function (
        target: Object,
        key: string | symbol,
        descriptor: PropertyDescriptor
    ) {
        const original = descriptor.value;

        descriptor.value = function (...args: any[]) {
            functions.forEach((func) => func(...args));

            original.apply(this, args);
        };

        return descriptor;
    };
}

export default class myMultipleCallbacksRoute extends BaseRoute {
    constructor() {
        super("/myMultipleCallbacksRoute");
    }

    @Multiple(
        function (_, __, next) {
            console.log("Middleware could be here :eyes:");
            next();
        },
        function (req, __, next) {
            if (req.body.id) {
                console.log(
                    "Maybe only return next callback only if req.body.id was provided :eyes:"
                );
            }

            next();
        }
    )
    async run(req, res) {
        res.send("Hello!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)