BAMIMI

v0.6.x

A powerful framework for Node.js that helps our great apps be built quickly, cleanly, securely, and efficiently

Cleaner

BAMIMI's clear architecture helps you code cleaner and tidier

More Efficient

Spend your time writing the code that really matters and let BAMIMI handle the rest.

Happier

When the problems have been solved, what are you waiting for to be unhappy?

"Bánh mì Việt Nam, đặc ruột thơm bơ"

Discover how BAMIMI applications are built.

Step 1: Install Cli:
npm install -g @knfs-tech/bamimi-cli
yarn global add @knfs-tech/bamimi-cli
Step 2: Generate project:
bamimi-cli app:generate <projectName> [options]
npx @knfs-tech/bamimi-cli@latest app:generate <projectName> [options]
Step 3: Start project in local:
cd <projectPath> && npm run dev
cd <projectPath> && yarn dev
Step 4: Build project:
npm run build
yarn build
npx @knfs-tech/bamimi-cli@latest build

Let's focus on the important part

Forget the days when it took a lot of time to build source code from scratch, all your time should be spent researching important features of the project, let BAMIMI take care of the rest.

Authenticate
Security
Mail
Job Queue
Task Scheduling
Socket.io
Interface
Testing
Docker

Authenticate

BAMIMI uses authentication with the fully-featured @knfs-tech/bamini-auth library. Additionally, BAMIMI's source includes authentication middleware that can be utilized on your router:

//src/routers/api/index.js
"use strict";
const router = require("express").
const authenticate = require("@iApp/http/middleware/apiAuthentication.mid")

router.get(
	"/demo", 
	authenticate, 
	require("@iApp/http/controllers/api/demo").index
);
Of course, you may define your own authentication middleware, allowing you to customize the authentication process. For more information on BAMIMI's authentication features, check out the authentication documentation.

Security

BAMIMI has installed many security libraries and is pre-installed in the kernel.
For example @knfs-tech/csrf library, you can use it by adding to your router:

//src/routers/web/index.js
"use strict";
const router = require("express").

const { protect: csrfProtect } = require("@knfs-tech/csrf")

// Router to get form
router.get(
	"/get-form",
	require("@iApp/http/controllers/web/demo").getForm
);

// Router to request form
router.post(
	"/save-form", 
	csrfProtect,
	require("@iApp/http/controllers/web/demo").saveForm
);
To add tokens to the form when submitting, you can use the available csrf utils:
Learn more

Mail

BAMIMI also provides you with an email notification generator that can be integrated with jobs

npx @knfs-tech/bamimi-cli@latest email:generate demoEmail -tn demoEmail -job demoEmail

After running command line, the system immediately creates the following files:

  • File demoEmail.email.js focus on handling issues of email sampling, preparing data for email samples and preparing email content. Then put it in the queue for sending
  • //src/app/emails/demoEmail.email.js
    "use strict";
    
    const { renderTemplate } = require("@iUtils/mail");
    const { QueueManager } = require("@knfs-tech/bamimi-schedule")
    module.exports = (data) => {
    const html = renderTemplate("demoEmail.ejs");
    
    QueueManager
    	.singleton()
    	.getQueue("demoEmail")
    	.add("demoEmail", {
    		to: data.email, 
    		subject: "Welcome to BAMIMI land", 
    		html: html});
    };
    
  • At demoEmail.job.js, The job performs retrieving data from the queue and handles sending email
  • //src/app/jobs/demoEmail.job.js
    "use strict";
    
    const { sendMail } = require("@iUtils/mail");
    module.exports = {
    	name: "demoEmail",
    	queue: "demoEmail",
    	handle: async function (job) {
    		await sendMail(job.data); 
    	}
    };
    
  • demoEmail.ejs is the default email template created by BAMIMI
  • Along with that, you need to go to file config of job and add the information of the job you want to run
    //src/configs/job.js
    "use strict";
    
    module.exports = [
        {
            name: "demoEmail",
            func: require("@iApp/jobs/demoEmail.job"),
            onMain: false //To set true if you want to run it concurrently on main processing
        },
    ];
    

    That's just a basic example of creating an email. You can see detailed usage with many cases at the the documentation

  • Job Queue

    BAMIMI also integrated queue jobs because we know the use case is very popular
    npx @knfs-tech/bamimi-cli@latest job:generate demoJob

    After running command line, the system immediately creates the following files:

    //src/app/jobs/demoJob.job.js
    "use strict";
    
    module.exports = {
        name: "demoJob",
        queue: "demoJob",
        handle: async function (job) {}
    };
    
    
  • Along with that, you need to go to file config of job and add the information of the job you want to run
    //src/configs/job.js
    "use strict";
    
    module.exports = [
        {
            name: "demoJob",
            func: require("@iApp/jobs/demoJob.job"),
            onMain: false //To set true if you want to run it concurrently on main processing
        },
    ];
    

    There are many more job properties and how to use them in practice, see here job queue documentation

  • Task Scheduling

    Effortlessly schedule recurring jobs and commands with a clear, expressive syntax—no more dealing with complex configuration files!

    npx @knfs-tech/bamimi-cli@latest job:generate cronJob
    ? Is it cron job (Y/n): 
    

    After running command line, the system immediately creates the following files and you add more information for schedule:

    //src/app/jobs/cronJob.job.js
    "use strict";
    
    module.exports = {
        name: "cronJob",
        queue: "cronJob",
    	schedules: [
    		{
    			name: "first-day-every-month",
    			time: {
    			pattern: "0 0 1 * *"
    			}, // cron schedule
    			prepare: { data: { email: "test@example.com" } },
    		},
    	],
        handle: async function (job) {}
    };
    
  • Like a normal job, you also need to declare it:
    //src/configs/job.js
    "use strict";
    
    module.exports = [
        {
            name: "cronJob",
            func: require("@iApp/jobs/cronJob.job"),
            onMain: false //To set true if you want to run it concurrently on main processing
        },
    ];
    

    finally you need to run it

    $ npm link .
    $ bamimi-enjoy-<projectName>-dev job cronJob
    
    Learn more
  • Socket

    BAMIMI integrated socket.io for handling real-time issue and event broadcasting issue with @knfs-tech/bamimi-socket.io

    For example, in the case of a processing message when processing an uploaded excel file:

  • Add the Auth middleware information, and the channel will handle event notifications
  • //src/routers/socket/index.js
    "use strict";
    
    const socket = require("@knfs-tech/bamimi-socket.io")
    
    const AuthMiddleware = require("@iApp/http/middleware/socketAuth.mid")
    
    const ImportChannel = require("@iApp/channels/import.channel")
    
    socket.on("/import", AuthMiddleware, ImportChannel.import)
    
  • Handle pushing notifications out
  • //src/app/channels/import.channel.js
    "use strict";
    
    const { getStorage } = require("@iKernel/cache")
    const cache = getStorage().new
    
    module.exports = {
        import: async function (io, socket) {
            const id = socket.handshake.currentUser.id;
            const room = `import-excel:${id}`
            await socket.join(room);
            await cache.subscribe(`import-excel-process-${id}`, async (err, count) => {
                if (err) {
                    console.error("Failed to subscribe to channels: ", err.message);
                } else {
                    console.log(`Subscribed successfully to ${count} channels.`);
                }
            })
            await cache.on("message", async (channel, message) => {
                const msgData = await JSON.parse(String(message))
                const event = "notify:"
                console.info("event: " + event)
                console.info("message: " + message)
                io.of("/import").to(room).emit("get-log", msgData);
            });
        }
    }
    
  • An example of a job processed through a file import process
  • //src/app/jobs/import.job.js
    "use strict";
    
    const { getStorage } = require("@iKernel/cache")
    const cache = getStorage().singleton
    
    module.exports = {
        name: "importJob",
        queue: "importQueue",
        handle: async function (job) {
            console.group("-------------- START IMPORT EXCEL --------------")
    		console.time("Process Time");
    
            const currentUser = job.data.currentUser;
    		for (let i = 0; i <= 100; i++) {
    			cache.publish(`import-excel-process-${id}`, JSON.stringify({
                        percentage: i,
                    }), (err, count) => {
                        if (err) {
                            console.error("Failed to publish: ", err.message);
                        } else {
                            console.log(`Message sent to ${count} subscriber(s).`);
                        }
                    })
    		}
    		console.info("--------------- END IMPORT EXCEL ---------------")
    		console.timeEnd("Process Time");
    		console.groupEnd();
        }
    }
    

    Interface

    BAMIMI fully develops interface three types API, Web UI, CLI.

  • With API is in structural form:
  • {
    	meta: {
    		"content": "OK", // content message
    		"code": 200 // status of response
    	},
    	data: {
    		userData: {
    			id: 1,
    			username: "demo12345",
    			email: "demao12345@gmail.com"
    		}
    	}
    }
    

    to get the response as above the programmer only needs create interface:

    npx @knfs-tech/bamimi-cli@latest itf:generate user

    then we have generated file, you need to custom it

    //@iApp/interfaces/apis/user.js
    "use strict";
    module.exports = (user) => {
    	return {
    		id: user.id,
    		username: user.username ?? null,
    		email: user.email ?? null
    	}
    }
    

    and declare it in:

    //@iApp/interfaces/apis/index.js
    /**
    * Set interface you want to use
    */
    module.exports = {
    	user: {
    		key: "userData", //representative field displayed at response
    		dto: require("./user") //format data
    	}
    }
    

    finally, we apply it in function of controller and router of api:

    //@iApp/controllers/api/demo.controller.js
    "use strict";
    module.exports = {
    	getUser: async function (req, res, next) {
    		try {
    			const user = {
    				id: 1,
    				username: "demo12345",
    				email: "demao12345@gmail.com"
    			}
    			
    			return res.status(200).sendUser(user) // or use default return res.status(200).sendData({userData: user})
    		} catch (error) {
    			next(error)
    		}
    	}
    }
    
    //@iRoutes/api/index.js
    "use strict";
    const router = require("express").Router();
    
    router.get("/get-user", require("@iApp/http/controllers/api/demo.controller").getUser);
    
    module.exports = router;
    
  • With Web UI integrated with EJS Engine:
  • //@iApp/controllers/web/demo.controller.js
    "use strict";
    
    module.exports = {
        index: async function (req, res, next) {
            try {
                return await res.view({
                    view: "pages/home", data: {
                        version: "1.0.0"
                    }
                });
            } catch (error) {
                next(error)
            }
        }
    };
    
  • With CLI integrated with Commander Library:
  • //@iRoutes/cli/index.js
    #!/usr/bin/env node
    const { Command } = require("commander");
    const program = new Command();
    
    const jobCommand = require("@iKernel/job");
    
    program
        .addCommand(jobCommand)
    
    program.parse(process.argv);
    
    and run by
    $ npm link .
    $ bamimi-enjoy-<projectName>-dev // for dev 
    $ bamimi-enjoy-<projectName>  // for build version
    
    Learn More

    Testing

    BAMIMI integrated Jest library for testing

    //src/app/services/sum.js
    "use strict";
    
    function sum(a, b) {
    	return a + b;
    }
    
    module.exports = sum;
    
    //test/units/services/sum.spec.js
    "use strict";
    
    const sum = require('@iApp/services/sum');
    
    test('adds 1 + 2 to equal 3', () => {
    	expect(sum(1, 2)).toBe(3);
    });
    

    Docker

    Deploying the project to different environments is very important, so we have created a docket file with three stages builder and development for future use and you can register it during project initialization

    $ npx @knfs-tech/bamimi-cli@latest create demo-project
    ? Would you like to include a Dockerfile? (Y/n)
    

    If you choose Yes (y/Y), the generated source code will include a DockerFile file with three stages

    Or you can create by CLI:

    npx @knfs-tech/bamimi-cli@latest docker:generate
  • Builder stage(use when building projects)
  • # Stage 1: Builder
    FROM node:20 AS builder
    
    # Install necessary build tools
    RUN apt-get update && apt-get install -y \
    build-essential \
    python3 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
    
    WORKDIR /usr/src/app
    
    COPY --chown=node:node package.json yarn.lock ./
    
    # Install dependencies for building
    RUN yarn install --production=false
    
    COPY --chown=node:node . .
    
    RUN yarn build
    
  • Development stage(use built resources from the builder stage and run in the development environment)
  • # Stage 2: Development
    FROM node:20-slim as development
    
    # Copy only the dist directory from the builder stage
    COPY --from=builder /usr/src/app/dist /usr/src/app/dist
    
    COPY --from=builder /usr/src/app/package.json /usr/src/app/package.json
    COPY --from=builder /usr/src/app/node_modules /usr/src/app/node_modules
    COPY --from=builder /usr/src/app/.env /usr/src/app/.env
    
    ENV NODE_ENV=development
    WORKDIR /usr/src/app
    
    RUN yarn install --production=false --ignore-scripts
    
    EXPOSE 3000
    
    CMD ["yarn", "start"]
    
  • Production stage(use built resources from the builder stage and run in the production environment)
  • # Stage 3: Production
    FROM node:20-slim as production
    
    # Copy only the dist directory from the builder stage
    COPY --from=builder /usr/src/app/dist /usr/src/app/dist
    
    COPY --from=builder /usr/src/app/package.json /usr/src/app/package.json
    COPY --from=builder /usr/src/app/.env /usr/src/app/.env
    
    ENV NODE_ENV=production
    WORKDIR /usr/src/app
    
    RUN yarn install --production
    
    EXPOSE 3000
    
    CMD ["yarn", "start"]
    
    Learn More

    We've only begun to explore the possibilities. With BAMIMI, you have everything you need to build a complete web application—email verification, linting, and even custom console commands. Dive into the documentation to continue your journey!

    We hope you will be happier ❤️

    We are also really happy to be able to help make your work simpler. We hope you have more time for yourself and will feel happy on your journey with BAMIMI.