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;
  // ...
}