Shared
codi-node-utils
The shared Node.js utilities package used by booki-api.
codi-node-utils is published as @codisolutions23/node-utils and imported by booki-api. It provides infrastructure utilities so they don't get duplicated across backend services.
Exports Reference
Database — MongoDB
import { useAtlas } from "@codisolutions23/node-utils";
// Connect on app startup — call once at boot
await useAtlas.connect({ uri: mongoUri, db: "mydb" });
// Get the Db instance
const col = useAtlas.getDb().collection<IUser>("users");
// Get the raw MongoClient
const client = useAtlas.getClient();
// Start a transaction session
const session = useAtlas.startSession();
// Close the connection on shutdown
await useAtlas.close();
Cache — Redis
import { useCache, useRedis } from "@codisolutions23/node-utils";
// useCache: high-level key/value operations
const { getCache, setCache, delCache, delCacheGroup } = useCache();
await setCache("key", value, 300); // TTL in seconds
const data = await getCache("key");
await delCache("key");
await delCacheGroup("users"); // delete all keys matching 'users:*'
// useRedis: initialize once at boot, then retrieve the ioredis client
useRedis().initialize({ host: "localhost", port: 6379, password: "secret" });
const redis = useRedis().getClient();
await redis.set("key", "value");
// Close the connection on shutdown
useRedis().disconnect();
Authentication
import {
authenticate,
requireAdmin,
requireRole,
signJwtToken,
} from "@codisolutions23/node-utils";
// Middleware factory — verifies Bearer token, optionally checks revocation
const authMiddleware = authenticate(accessTokenSecret, existsByJti);
// Admin-only middleware factory
const adminMiddleware = requireAdmin(accessTokenSecret, existsByJti);
// Role-based middleware factory
const ownerMiddleware = requireRole("owner", { secretKey: accessTokenSecret });
// Sign a JWT — all params in a single object
const token = signJwtToken({
payload: { id: userId, role: "owner" },
secretKey: accessTokenSecret,
signOptions: { expiresIn: "15m" },
});
Passwords & Tokens
import {
comparePasswords,
hashPassword,
hashToken,
} from "@codisolutions23/node-utils";
const match = await comparePasswords(plainText, hash);
const hashed = await hashPassword(plainText); // bcrypt hash for storing passwords
const tokenHash = hashToken(rawRefreshToken); // SHA-256 hash for refresh tokens at rest
HTTP Errors
import {
HttpError,
BadRequestError,
UnauthorizedError,
ForbiddenError,
NotFoundError,
ConflictError,
UnprocessableEntityError,
InternalServerError,
} from "@codisolutions23/node-utils";
throw new NotFoundError("User not found.");
throw new ConflictError("Email already in use.");
throw new UnprocessableEntityError(joiError.message);
Error Handler Middleware
import { errorHandler } from "@codisolutions23/node-utils";
// Register last in app.ts
app.use(errorHandler);
Pagination
import { paginate } from "@codisolutions23/node-utils";
// Called inside base.repo.ts after an aggregate query
const result = paginate(items, page, limit, total);
// Returns: { items, pages, pageRange } e.g. pageRange = '1-10 of 42'
Utilities
import { buildCacheKey, toObjectId, logger } from "@codisolutions23/node-utils";
// Deterministic Redis key from collection + params
const key = buildCacheKey("users", { organizationId, page, limit });
// Convert string → MongoDB ObjectId safely
const id = toObjectId(stringId);
// Winston logger
logger.info("Server started");
logger.error("Unexpected failure", { error });
Email & Storage
import {
useMailer,
renderHandlebarsTemplate,
useS3,
} from "@codisolutions23/node-utils";
// Instantiate the mailer with SMTP config
const mailer = new useMailer({ email, password, host, port, secure });
// Render a Handlebars template by absolute file path
const html = await renderHandlebarsTemplate({
filePath: getTemplatePath("emails", "booking-confirmation"),
context: data,
});
// Send the email — sender is optional (defaults to config email)
await mailer.sendMail({
to,
subject,
html,
sender: "Booki <noreply@booki.app>",
});
// Instantiate the S3 client with config
const s3 = new useS3({
accessKeyId,
secretAccessKey,
endpoint,
region,
bucket,
forcePathStyle: true,
});
// Upload a file
await s3.uploadObject({
key: "avatars/user-1.png",
body: buffer,
contentType: "image/png",
});
// Delete a file
await s3.deleteObject("avatars/user-1.png");
Cron Jobs
import { scheduleCronJob } from "@codisolutions23/node-utils";
scheduleCronJob("0 0 * * *", "Asia/Manila", async () => {
// Runs daily at midnight in the given timezone
await cleanExpiredTokens();
});
Request Types
import type { AuthenticatedRequest } from "@codisolutions23/node-utils";
// Extends Express Request with:
// req.user — decoded JWT payload
// req.token — raw Bearer token string
async function getProfile(
req: AuthenticatedRequest,
res: Response,
next: NextFunction,
) {
const userId = req.user!._id;
// ...
}
