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
);
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
);
Caching
BAMIMI supports 3 types of cache: Local cache, redis and memcached.
For example local cache @knfs-tech/bamimi-cache library, you can use it to cache in localstorage:
//src/http/controllers/api/demo.controller.js
"use strict";
const Cache = require("@iKernel/cache")()
const cache = Cache.singleton;
module.exports = {
index: async function (req, res, next) {
try {
cache.set("user", {
id: 1,
name: "demo user",
age: 12
})
const user = {
id: 1,
name: "demo user",
age: 12
}
return res.status(200).sendUser(user);
} catch (error) {
next(error)
}
}
};
//src/configs/cache.js
//above config
local: {
folder: path.join(__dirname, '../../storage/cache'), // Directory for storing cache files
expire: 0, // Cache expiration time in seconds (0 means no expiration)
autoCompress: false, // Whether to compress cache files automatically
log: false, // Enable logging for cache operations
peakDuration: 2000, // Maximum duration for cache peak
maxSize: 0, // Maximum size for cache (0 means no size limit)
},
//below config
File Storage
BAMIMI supports all types of storage and storage services.
//src/routes/api/file.js
const Storage = require("@iKernel/file")()
const FileController = require("@iApp/http/controllers/apis/file.controller")
router.post("/images",
(Storage.upload({
options: {
limits: { fileSize: 10 * 1024 * 1024 },
filename: (req, file, cb) => {
const userId = req.session.currentUser.id;
const id = req.body.id ?? '';
const fileN = id + '-' + 'u' + '-' + userId + '-' + Date.now().toString() + '-' + file.originalname;
file.aliasName = fileN
if (cb) {
cb(null, fileN);
}
},
fileFilter: (req, file, cb) => {
const allowedTypes = /jpeg|jpg|png|/;
const isFileTypeValid = allowedTypes.test(file.mimetype);
if (isFileTypeValid) {
cb(null, true);
} else {
cb(new Error('Invalid file type. Only JPG, JPEG, PNG, and GIF files are allowed.'), false);
}
}
}
})).single('file'),
FileController.upload)
//src/configs/file.js
module.exports = {
use: process.env.STORAGE || "local", // Storage option to use (can be "local", "s3", "blob", or "gcStorage")
defaultLimits: {
fileSize: 1024 * 1024 // Maximum file size for uploads (1MB)
},
storages: {
local, // Local storage configuration
s3, // S3 storage configuration
blob: {
...blob,
connectString:
`DefaultEndpointsProtocol=${blob.defaultEndpointsProtocol};AccountName=${blob.accountName};AccountKey=${blob.accountKey};EndpointSuffix=${blob.endpointSuffix}`, // Azure connection string for Blob storage
},
gcStorage, // Google Cloud Storage configuration
}
};
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:
//src/app/emails/demoEmail.email.js
"use strict";
const { renderTemplate } = require("@iKernel/mail");
const QueueManager = require("@iKernel/queue")()
module.exports = (data) => {
const html = renderTemplate("demoEmail.ejs");
QueueManager.getQueue("emailQueue").add("sendMail", {
to: data.email,
subject: "Welcome to Bamimi land",
html: html
});
}
//src/app/jobs/demoEmail.job.js
"use strict";
const { sendMail } = require("@iKernel/mail");
module.exports = {
name: "demoEmail",
queue: "demoEmail",
queueManager: "bullmq",
handle: async function (job) {
await sendMail(job.data);
}
};
//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 popularnpx @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",
queueManager: "bullmq",
handle: async function (job) {}
};
//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",
queueManager: "bullmq",
schedules: [
{
name: "first-day-every-month",
time: {
pattern: "0 0 1 * *"
}, // cron schedule
prepare: { data: { email: "test@example.com" } },
},
],
handle: async function (job) {}
};
//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
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:
//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)
//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);
});
}
}
//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.
{
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;
//@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)
}
}
};
//@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);
$ npm link .
$ bamimi-enjoy-<projectName>-dev // for dev
$ bamimi-enjoy-<projectName> // for build version
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
# Stage 1: Builder
FROM node:22 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 package.json and yarn.lock for dependency installation
COPY --chown=node:node package.json yarn.lock ./
# Install dependencies for building
RUN yarn install --production=false
# Copy the application code
COPY --chown=node:node . .
# Set proper permissions for the storage folder
RUN mkdir -p storage && chmod -R 775 storage && chown -R node:node storage
RUN yarn add @knfs-tech/bamimi-cli -D
# Build the application
RUN yarn build
# Stage 2: Development
FROM node:22-slim as development
# Set working directory
WORKDIR /usr/src/app
# Copy necessary files from 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
COPY --from=builder /usr/src/app/storage /usr/src/app/storage
# Install all dependencies (including devDependencies)
RUN yarn install --production=false --ignore-scripts
ENV NODE_ENV=development
EXPOSE 3000
CMD ["yarn", "start"]
# Stage 3: Production
FROM node:22-slim as production
# Set working directory
WORKDIR /usr/src/app
# Copy only necessary files from 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
COPY --from=builder /usr/src/app/storage /usr/src/app/storage
# Install production dependencies
RUN yarn install --production
# Set environment variables
ENV NODE_ENV=production
# Expose the application port
EXPOSE 3000
# Run the application
CMD ["yarn", "start"]
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!