@krvinay/express_api
v2.1.0
Published
Node API Application server
Maintainers
Readme
Express API Microservice
A comprehensive Node.js API microservice framework built on Express, MySQL2, and Sequelize. This package provides a complete, production-ready setup to rapidly develop robust APIs — in JavaScript or TypeScript.
🆕 What's new in v2.1.0
- Breaking:
mysql.dbandmysql.dbConnectionremoved from the standalone@krvinay/express_api/mysqlexport — usemysql.getConnectionORM('default')instead (see Database access).req.db/req.dbConnection/req.db.getConnectionon the per-requestreqobject keep working (but see the deprecations below) req.getConnectionORM()— new function that loads Sequelize models into an isolated object (orm.<ModelName>) instead of the sharedreq.db, so loading a model folder for an ad-hoc/tenant connection can't overwrite models loaded elsewhere. Works for configured databases too —req.getConnectionORM('default')— with its own independent model bindings (see Database access)- Ad-hoc database credentials —
getConnection()now accepts a credentials object to connect to a database that isn't declared insrc/config/database.js, with automatic per-credential connection caching (see Database access) disableSequelizeORMconfig flag — setdisableSequelizeORM: trueinappConfig.jswhen you only need raw SQL:req.dbis then not created at all (undefined). Database connections are still opened, andreq.getConnection()/ on-demandreq.getConnectionORM()keep working regardless- Deprecated:
req.dbandreq.dbConnection()— everything exposed onreq.db(models,sequelize,Op,QueryTypes,<db_name>_connection,getConnection) and thereq.dbConnection()helper are deprecated and will be removed in v3.0.0. Usereq.getConnectionORM(db_name?)for models/Op/QueryTypesandreq.getConnection(db_name?).connectionfor the raw Sequelize instance. Both still work throughout v2.x — a one-timeDeprecationWarningis emitted on first use of each - Fix: Sequelize model loading now runs synchronously and fully completes during startup, closing a race window where requests could arrive before
req.dbwas populated - Fix:
util.pluralize/util.singularize— uninflected words (sheep,fish,news,series, …) were being inflected anyway, and some singularize rules leaked a literal$2into the result (series→s$2eries) - Fix:
util.get_ipv4_addrnow finds the IPv4 address in a comma-separated list (e.g. anX-Forwarded-Forchain) — previously only the first comma was handled — and non-numeric segments (a.b.c.d) are no longer accepted as IPv4 - Fix:
util.generate_password— a charset typo duplicatedaand madezunreachable in alphanumeric passwords - Fix:
util.underscoreno longer throws on strings without an uppercase letter;util.format/req.formatMessageno longer drop falsy placeholder values (0,false) - Fix:
res.json(null)(or any JSON scalar) no longer crashes the auto-format response pipeline — scalars are wrapped asdata - Fix: thread calls without a callback no longer crash the parent process when the child responds; error-handler redirects URL-encode the error message; nested
req.writeLogpaths ('payments/refunds') now work on Windows too
- First-class TypeScript support — every application file can be
.jsor.ts; the installer scaffolds TypeScript projects and full typings ship with the package (see TypeScript Support) npx express-api init— interactive scaffolding CLI (source dir, language, Express version) that scaffolds the project, records the dependencies at your chosen versions inpackage.json, and installs them — nothing runs automatically onnpm install- Hardened runtime — thread workers report crashes instead of hanging, CORS preflight no longer crashes on missing config, invalid HTTP codes are sanitized,
util.mt_rand/generate_passworduse cryptographically secure randomness, and the undeclareduuiddependency was replaced with Node's built-incrypto.randomUUID - Your project owns its dependencies —
express,sequelize, andmysql2are no longer pre-installed by npm;npx express-api initinstalls them at the versions you choose and records them in yourpackage.json(see Installation) - Express 4 and 5 compatible — the accepted
expressrange is^4.21.2 || ^5.0.0 - Async route errors are caught — a rejected
asynchandler is forwarded to the framework's error handler and returns the standard error envelope, on Express 4 too (which natively ignores handler promises).try/catch + next(err)still works and is still recommended for custom handling - Breaking: the deprecated
util,threads, andmysqlproperties on the main export were removed — see Migrating to v2
🚚 Migrating to v2
The properties attached to the main export (deprecated since v1) were removed. Import the subpath modules instead:
// ❌ v1 (removed)
const api = require("@krvinay/express_api");
api.util.pluralize('user');
api.threads('index', 'main', payload, headers, callback);
api.mysql.getConnection();
// ✅ v2
const util = require("@krvinay/express_api/util");
const threads = require("@krvinay/express_api/threads");
const mysql = require("@krvinay/express_api/mysql");
util.pluralize('user');
threads('index', 'main', payload, headers, callback);
mysql.getConnection();Everything else is backward compatible: server.use(require("@krvinay/express_api")), the req API, response envelope, configs, and scaffolded project layout are unchanged.
🚚 Migrating to v2.1
Only the standalone @krvinay/express_api/mysql export changed — .db and .dbConnection were removed from it. Replace the old model-loading pattern with getConnectionORM:
// ❌ v2.0 (removed, standalone mysql export only)
const models = mysql.db;
models.connection = models.getConnection();
module.exports = models;
// ✅ v2.1
module.exports = mysql.getConnectionORM('default');req.db, req.dbConnection(), and req.db.getConnection() on the per-request req object keep working unchanged in v2.1 — no immediate changes needed there. Note that req.db and req.dbConnection() are now deprecated (removal planned for v3.0.0), so prefer req.getConnectionORM() and req.getConnection().connection in new code.
Everything else is unchanged: req.db.<ModelName>, req.getConnection(db_name), route/model/config conventions, and the scaffolded project layout all continue to work as before.
✨ Features
- 🚀 Guided Setup -
npx express-api initscaffolds a complete project structure - 📊 Auto Response Formatting - Consistent API response structure out of the box
- 🗄️ Flexible Database Support - Powered by MySQL2 and Sequelize with connection pooling. Supports MySQL1-style callback query syntax (
connection.query(sql, callback)) without any code changes - 🛠️ Configurable Error Messages - Customize database error messages to match your needs
- 🌍 Multilingual Support - Built-in internationalization for response messages
- 📝 Response Logger Hook - Configurable logging for all API responses
- ⚡ Thread Support - Execute functions asynchronously with minimal code changes. Accepts an optional
timeout(ms) — if the child process does not respond in time it is killed and the callback receives an error - 🔍 Thread Logging - Enhanced debugging capabilities for threaded operations
- 📚 Helper Function Library - Pre-built utilities to accelerate API development
- 📄 File Logging - Generate log files instead of console output
- 🔐 Response Header Management - Built-in CORS and header configuration (no additional packages needed)
- 🟦 First-class TypeScript Support - Write your app in JavaScript or TypeScript (or mix both). All application files (
appConfig,database, routes, models, helpers, lang, activities) can be.jsor.ts.npx express-api initscaffolds.tssources for TypeScript projects, and full typings (including the enrichedreqobject) ship with the package - 🤖 Claude AI Agent Ready - Ships with
CLAUDE.md— a complete agent guide so Claude Code knows your project's exact API patterns, conventions, and architecture without any prompting
📦 Installation
Project dependencies — installed by init, not by npm
Installing @krvinay/express_api pulls in only the framework itself — zero dependencies. express, sequelize, mysql2, and dotenv are declared as optional peers, so npm does not pre-install them — instead, npx express-api init asks which Express version you want and then installs everything at your chosen versions:
express—4,5, or an exact version, exactly as you answered (both Express 4^4.21.2and Express 5^5.0.0are supported; rejectedasyncroute handlers reach the error handler on either)sequelize,mysql2,dotenv— kept if already installed/declared, otherwise the latest release- TypeScript projects additionally get
typescript,ts-node,@types/node, and@types/express(matching your Express major) asdevDependencies
Everything is recorded in your package.json, so your project fully owns the versions, and init installs them in the foreground before it returns. Until init has run, the framework cannot be required — its error message will remind you to run it.
Scaffolding — complete the setup with init
npm install only installs the package. Run npx express-api init once to complete the setup:
npm install @krvinay/express_api
npx express-api initinit asks for the source directory, the language (js/ts), and the Express version (4, 5, or a specific version); press Enter to accept the shown defaults. It then scaffolds the project, sets main/start in your package.json, records the dependencies at your chosen versions, and installs them — after init finishes, npm start just works. Should you forget to run it, requiring the framework tells you exactly that.
Flags for non-interactive use:
npx express-api init --yes # accept defaults, no questions
npx express-api init --lang=ts # force TypeScript templates
npx express-api init --src=app # custom source directory
npx express-api init --express=4 # use Express 4 (also accepts 5 or a specific version like 4.21.2)Notes:
- The Express question is only asked when your
package.jsondoesn't declare express yet, and its default is whatever is already installed — nothing gets upgraded implicitly. Answering4or5installs the newest release of that major; the explicit--expressflag overrides a previously recorded version. - Your choices are persisted: the language lands in
.envasSRC_LANG(next toSRC), the versions inpackage.json. - Only missing files are ever created — existing files are never overwritten, and re-running
initis always safe. - Flags,
--yes, or a non-interactive shell (scripts, CI) skip the prompts and use defaults / env vars.
Instead of the interactive prompts or CLI flags, npx express-api init can also be configured via environment variables or a .env file. If nothing is set, defaults apply.
| Variable | Description | Default | Options |
|------------|------------------------------|---------|----------------|
| SRC | Application source directory | src | Any valid path |
| SRC_LANG | Language of scaffolded files | auto | js / ts (auto-detects ts when a tsconfig.json exists) |
Method 1: Via env — Linux / macOS
SRC=src SRC_LANG=ts npx express-api init --yesMethod 2: Via env — Windows (CMD or PowerShell)
# Install cross-env if not already installed
npm install --save-dev cross-env
npx cross-env SRC=src SRC_LANG=ts npx express-api init --yesMethod 3: Via .env file
Create a .env file in your project root before running init:
NODE_ENV=dev
SRC=src
SRC_LANG=jsThen run npx express-api init --yes.
🚀 Getting Started
Fresh Installation
- Create a new project directory:
mkdir my-api-project
cd my-api-project- Initialize npm:
npm init -y- Install the package:
npm install @krvinay/express_api- Complete the setup (asks src dir, language & Express version, then installs everything):
npx express-api init- Start your application:
npm startIntegrating with Existing Projects
If you're installing this package in an existing project, you'll need to:
- Edit
routes/index.jsto integrate with your existing route structure - Merge any conflicting configuration files
[!TIP] Set
SRC="/"to skip source directory creation. The package will treat your root directory as the source directory.
📁 Project Structure
The installer generates the following structure:
project-root/
├── src/ # Source directory (configurable via SRC)
│ ├── activities/ # Thread-related files and functions
│ │ └── index.js # Sample thread function
│ ├── config/ # Configuration files
│ │ ├── appConfig.js # Main configuration file
│ │ └── database.js # Database connection config
│ ├── helpers/ # Utility function library
│ │ └── index.js # Helper functions
│ ├── lang/ # Internationalization files
│ │ ├── en.js # English translations
│ │ └── hi.js # Hindi translations
│ ├── logs/ # Log files directory
│ ├── models/ # Database models
│ │ ├── datasource/ # Sequelize table schemas
│ │ └── index.js # Database connection (MySQL/Sequelize)
│ └── routes/ # API route definitions
│ └── index.js # Main route file
├── .env # Environment variables
├── .gitignore # Git ignore rules
├── README.md # Project documentation
├── tsconfig.json # (TypeScript projects only)
└── server.js # Application entry pointIn TypeScript projects all of the above source files are scaffolded as .ts (server.ts, appConfig.ts, …). The framework loads .js, .cjs, .ts, and .cts files interchangeably, so you can also mix languages within one project.
⚙️ Configuration
For New Projects
npx express-api init creates a server.js file for you. Simply run:
node server.jsFor Existing Projects
[!NOTE] If you already have a
server.jsfile, update it with the following code:
require("dotenv").config();
const server = require('express')();
const { json, urlencoded } = require('express');
const default_port = 8080;
server.use(json({ limit: '50mb', extended: true }));
server.use(urlencoded({ limit: '50mb', extended: true }));
server.use(require("@krvinay/express_api"));
server.listen(process.env.PORT || default_port, () => {
console.log(`Server running on port ${process.env.PORT || default_port}`);
});🟦 TypeScript Support
The framework works with JavaScript and TypeScript projects — no build step required.
New TypeScript project
mkdir my-api-project && cd my-api-project
npm init -y
npm install @krvinay/express_api
npx express-api init --lang=ts # scaffolds .ts sources and installs everything
npm start # runs ts-node server.tsinit --lang=ts creates .ts sources (server.ts, src/config/appConfig.ts, src/routes/index.ts, …), creates a tsconfig.json if missing, and sets "start": "ts-node server.ts". It also records and installs the TypeScript toolchain as devDependencies — typescript, ts-node, @types/node, and @types/express (matching your Express major). Versions you already have installed or declared are always kept (never upgraded); anything missing is installed at its latest published version. If your project already has a tsconfig.json, TypeScript is detected automatically — no --lang needed.
How it runs
Every application file (appConfig, database, routes, models, helpers, lang files, activities) may be .js, .cjs, .ts, or .cts — the framework resolves whichever exists. Both module.exports = … and export default … are supported. You can run the app with any of:
ts-node server.ts/tsx server.ts— TypeScript entry pointnode server.js— plain Node entry point; when the framework encounters a.tsapplication file it transparently registersts-node(ortsx/@swc-node/register, whichever is installed) in transpile-only mode. Type checking is your project's job (npx tsc --noEmit).
Background activities (threads) written in TypeScript work too — the forked worker loads them through the same loader.
Typings
The package ships full type declarations, including an Express Request augmentation, so req.data, req.db, req.getConnection(), req.writeLog(), req.formatMessage() etc. are all typed in your route handlers:
import { Router, Request, Response, NextFunction } from 'express';
const routes = Router();
routes.get('/users', async (req: Request, res: Response, next: NextFunction) => {
try {
const conn = req.getConnection(); // typed MySqlUtil wrapper
const users = await conn.querySync('SELECT * FROM users WHERE id = ?', req.data.id);
res.json({ data: { users } });
} catch (err) {
next(err);
}
});
export default routes;🔧 Environment Configuration
# Node Application environment
NODE_ENV=dev
# Server Configuration
PORT=8080
# Source Directory (default: src)
SRC=src
# Database Configuration (add your database credentials)
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=
DB_NAME=your_database📖 Usage
Once installed and configured, you can start building your API by:
- Adding Routes - Define your endpoints in
src/routes/index.js(or.ts) - Creating Models - Add database models in
src/models/ - Writing Helpers - Create utility functions in
src/helpers/ - Configuring Languages - Add translations in
src/lang/ - Implementing Threads - Add background tasks in
src/activities/
Package exports
| Import | Provides |
|--------|----------|
| @krvinay/express_api | The framework middleware to mount with server.use(...) |
| @krvinay/express_api/util | Static utility helpers (pluralize, md5, uuid, generate_password, date helpers, …) |
| @krvinay/express_api/threads | threads(activity, execFunction, payload, headers, callback, timeout?) — run an activity in a child process |
| @krvinay/express_api/mysql | Standalone database access (getConnection(), getConnectionORM()) outside a request context |
// JavaScript
const util = require("@krvinay/express_api/util");
const threads = require("@krvinay/express_api/threads");
const mysql = require("@krvinay/express_api/mysql");// TypeScript
import util from "@krvinay/express_api/util";
import threads from "@krvinay/express_api/threads";
import mysql from "@krvinay/express_api/mysql";🗄️ Database access
By default, every database in src/config/database.js is connected, and (when bindDatabase is true and disableSequelizeORM is not set) req.db is mapped exactly as in v2.0.0: the Sequelize models of every configured database merged together (req.db.<ModelName>), plus sequelize, Op, QueryTypes, one <db_name>_connection per database, and the getConnection alias.
[!WARNING]
req.dbandreq.dbConnection()are deprecated and will be removed in v3.0.0. Everything has a direct replacement:req.getConnectionORM(db_name?)returns the same models plussequelize/Op/QueryTypes/connectionas an isolated object, andreq.getConnection(db_name?).connectionis the raw Sequelize instance. Both keep working for the whole v2.x line; the first use of each emits a one-timeDeprecationWarning.
Set disableSequelizeORM: true in appConfig.js if you just need raw SQL — req.db is then not created at all (req.db is undefined). Database connections are still opened, and req.getConnection() and on-demand req.getConnectionORM() calls keep working regardless:
req.config = {
disableSequelizeORM: true, // req.db is not created; getConnection/getConnectionORM still work
};req.getConnection(db_name?) returns a MySqlUtil wrapper for raw SQL. It accepts either a configured db_name, or a credentials object to connect to a database outside src/config/database.js; ad-hoc connections are cached per unique credential set, so repeated calls with the same credentials reuse one connection instead of opening a new pool each time. The raw Sequelize instance is always available via .connection:
const conn = req.getConnection(); // default db
const conn = req.getConnection('analytics'); // named db from database.js
const conn = req.getConnection({ host, username, password, database }); // ad-hoc credentials
const rawSequelize = conn.connection; // raw Sequelize instancereq.dbConnection(db_name?) returns the raw Sequelize instance directly for a configured db_name — deprecated, removed in v3.0.0 (as is its req.db.getConnection alias). Use req.getConnection(db_name?).connection instead, which also supports ad-hoc credentials:
const rawSequelize = req.getConnection().connection; // default db (preferred)
const rawSequelize = req.getConnection('analytics').connection; // named db from database.js
// deprecated equivalents, still working in v2.x:
const rawSequelize = req.dbConnection(); // default db
const rawSequelize = req.dbConnection('analytics'); // named db from database.jsreq.getConnectionORM(db_name?) loads Sequelize models into their own isolated object — accessible as orm.<ModelName> — instead of the shared req.db. Pass a configured db_name to reuse its connection with independent model bindings, or ad-hoc credentials with a required models field naming the folder to load:
const orm = req.getConnectionORM('default'); // isolated copy, own connection
const tenantOrm = req.getConnectionORM({ host, username, password, database, models: 'default' }); // ad-hoc, `models` required
const user = await orm.User.findOne({ where: { id: req.data.id } });Results are cached per target, so repeated calls with the same db_name, or the same credentials + models, return the same object. Throws if a credentials object omits models. Use this instead of loading models onto ad-hoc connections through the shared req.db — since req.db is one object shared by every request, two connections loading a model of the same name would otherwise overwrite each other.
[!NOTE] The standalone
@krvinay/express_api/mysqlexport (used outside a request context) exposes exactly two functions —getConnection()andgetConnectionORM()— and nothing else; it has no.dbor.dbConnection. Both accept a configureddb_nameor ad-hoc credentials, both give you the raw Sequelize instance via.connection, and models loaded throughgetConnectionORM()are isolated per target. Usemysql.getConnectionORM('default')where you previously usedmysql.db.
🤖 Claude AI Agent
Every project scaffolded by this package includes CLAUDE.md at its root — a complete agent guide that instructs Claude exactly how to write APIs using this framework's patterns and conventions.
What it covers
- Full
reqobject API reference (req.data,req.db,req.getConnection,req.util,req.getEnv,req.writeLog,req.formatMessage, …) - Standard response envelope format and all shape rules
- Route writing conventions in JS and TS
- Raw SQL via
MySqlUtiland Sequelize ORM patterns appConfiganddatabaseconfig file templates- i18n message key patterns with auto-translation
- Activity/thread pattern with
(req, payload)signature - Logging, security headers, common patterns, and anti-patterns
Activate with Claude Code
Copy CLAUDE.md into .claude/ to have Claude Code load it automatically as project context every time you open the project:
mkdir -p .claude
cp CLAUDE.md .claude/CLAUDE.mdClaude will then follow all conventions automatically — no prompting needed.
👤 Author
Vinay Kumar
- Package: @krvinay/express_api
🆘 Support
For issues and questions, please visit the GitHub repository or open an issue.
