dynamic-api-builder-js
dynamic-api-builder-js
Configuration-driven REST API generator for Node.js — define your endpoints in JSON, get a fully working Express API instantly.
Table of Contents
- Overview
- Installation
- Quick Start
- Configuration
- API Types
- Hooks
- Validation
- Filters & Pagination
- Joins
- Response Format
- Advanced Usage
- API Reference
Overview
dynamic-api-builder-js (DAB) eliminates the boilerplate of writing Express routes, SQL queries, and request validation by hand. You declare your API endpoints as a JSON config, and DAB registers them on an Express router automatically — including CRUD operations, multi-step transactions, database function/procedure calls, and fully custom handlers.
Supports MySQL and PostgreSQL.
Installation
npm install dynamic-api-builder-jsPeer dependencies (install separately):
# For MySQL
npm install mysql2
# For PostgreSQL
npm install pgQuick Start
const express = require('express');
const dabApi = require('dynamic-api-builder-js');
const app = express();
app.use(express.json());
const { router } = dabApi({
database: {
type: 'mysql', // 'mysql' | 'postgres'
host: 'localhost',
port: 3306,
user: 'root',
password: 'secret',
database: 'mydb',
},
apis: [
{
id: 'list-users',
endpoint: '/users',
method: 'GET',
type: 'CRUD',
operation: 'READ',
table: 'users',
columns: ['id', 'name', 'email'],
pagination: true,
},
{
id: 'create-user',
endpoint: '/users',
method: 'POST',
type: 'CRUD',
operation: 'CREATE',
table: 'users',
columns: ['name', 'email', 'password'],
},
],
});
app.use('/api', router);
app.listen(3000, () => console.log('Server running on port 3000'));Configuration
Database Config
Pass a database object at the top level of your config. DAB creates and manages the connection pool for you.
| Field | Type | Default | Description |
|---|---|---|---|
type |
string |
"mysql" |
"mysql" or "postgres" |
host |
string |
"localhost" |
Database host |
port |
number |
3306 |
Database port |
user |
string |
"root" |
Database user |
password |
string |
"" |
Database password |
database |
string |
"" |
Database/schema name |
connectionLimit |
number |
10 |
Max connections in the pool |
API Config
Each entry in the apis array describes a single route.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
No | Identifier used in logs and warnings |
endpoint |
string |
Yes | Express route path (e.g. /users/:id) |
method |
string |
Yes | HTTP method: GET, POST, PUT, DELETE, etc. |
type |
string |
Yes | API type (see API Types) |
validation |
object |
No | Validation rules (see Validation) |
hooks |
object |
No | before / after hook names (see Hooks) |
API Types
CRUD
Handles standard Create, Read, Update, Delete operations against a single table.
{
id: 'get-user',
endpoint: '/users/:id',
method: 'GET',
type: 'CRUD',
operation: 'READ', // 'CREATE' | 'READ' | 'UPDATE' | 'DELETE'
table: 'users',
columns: ['id', 'name', 'email'],
filters: ['status', 'role'], // query params used as WHERE filters
pagination: true, // enables ?page=&limit= query params
joins: [ // optional JOIN clauses
{
type: 'LEFT', // 'INNER' | 'LEFT' | 'RIGHT'
table: 'roles',
condition: 'users.role_id = roles.id'
}
],
filterConfig: {
name: { operator: 'LIKE' }, // override filter operator
orFields: ['name', 'email'] // fields matched by ?_or= query param
}
}Operations:
| Operation | HTTP Method | Description |
|---|---|---|
READ |
GET |
Fetch rows; supports filter/pagination |
CREATE |
POST |
Insert a new row |
UPDATE |
PUT/PATCH |
Update row by :id |
DELETE |
DELETE |
Delete row by :id |
TRANSACTION
Executes multiple database steps as an atomic transaction. Steps run in order; each step's result is passed to the next via map.
{
id: 'transfer-funds',
endpoint: '/transfer',
method: 'POST',
type: 'TRANSACTION',
steps: [
{
operation: 'CREATE',
table: 'ledger',
columns: ['account_id', 'amount', 'type'],
},
{
operation: 'UPDATE',
table: 'balances',
columns: ['balance'],
map: {
// Map a value from a previous step's result
// "$stepResultKey.fieldName"
balance: '$0.amount'
}
}
]
}A step can also use a custom handler instead of a CRUD operation:
{ handler: 'myCustomHookName' }CALL_FUNCTION
Calls a database scalar function and returns its result.
{
id: 'get-discount',
endpoint: '/discount',
method: 'GET',
type: 'CALL_FUNCTION',
function: 'calculate_discount', // DB function name
params: [
{ name: 'user_id', source: 'query' },
{ name: 'coupon', source: 'body' },
{ name: 'currency', source: 'static', value: 'INR' }
]
}Parameter sources:
| Source | Reads from |
|---|---|
body |
req.body[name] |
query |
req.query[name] |
params |
req.params[name] |
headers |
req.headers[name] |
static |
Hardcoded value in config |
CALL_PROCEDURE
Calls a stored procedure. Returns a single result set or an array of result sets when the procedure returns multiple.
{
id: 'sync-inventory',
endpoint: '/inventory/sync',
method: 'POST',
type: 'CALL_PROCEDURE',
procedure: 'sync_inventory_proc',
params: [
{ name: 'warehouse_id', source: 'body' },
{ name: 'triggered_by', source: 'static', value: 'api' }
]
}FUNCTION
Executes a fully custom JavaScript handler — useful for complex logic that doesn't fit the other types.
const { hooks } = dabApi({ ... });
hooks.register('myHandler', async (req) => {
// any custom logic
return { message: 'done' };
});
// In your API config:
{
id: 'custom-endpoint',
endpoint: '/custom',
method: 'POST',
type: 'FUNCTION',
handler: 'myHandler'
}Hooks
Hooks let you run code before or after the main operation executes — great for password hashing, logging, or enriching response data.
const { hooks } = dabApi(config);
// Register a hook
hooks.register('hashPassword', async (req) => {
if (req.body.password) {
req.body.password = await bcrypt.hash(req.body.password, 10);
}
});
// Use in API config
{
type: 'CRUD',
operation: 'CREATE',
table: 'users',
columns: ['name', 'email', 'password'],
hooks: {
before: 'hashPassword', // runs before the DB operation
after: 'sendWelcomeEmail' // runs after, receives (req, result)
}
}Built-in hooks (registered automatically):
| Hook Name | Description |
|---|---|
hashPassword |
Hashes req.body.password using bcrypt before saving |
Hook methods on DabHooks:
hooks.register('name', fn) // register a single hook
hooks.registerBatch({ name: fn }) // register multiple at once
hooks.has('name') // check if a hook exists → boolean
hooks.remove('name') // unregister a hookValidation
Add a validation object to any API config to enforce rules on request fields.
{
type: 'CRUD',
operation: 'CREATE',
table: 'users',
columns: ['name', 'email'],
validation: {
name: { required: true, minLength: 2, maxLength: 100 },
email: { required: true, type: 'email' },
age: { type: 'number', min: 18 },
}
}Validation failures return HTTP 400 with a structured error:
{
"success": false,
"error": {
"message": "Validation failed",
"code": 400,
"details": [ ... ]
},
"timestamp": "2026-06-22T10:00:00.000Z"
}Filters & Pagination
Filters
List field names in the filters array. DAB maps them from query parameters to SQL WHERE clauses automatically.
GET /users?status=active&role=admin
Use filterConfig to customize operators:
filterConfig: {
name: { operator: 'LIKE' }, // WHERE name LIKE '%value%'
age: { operator: '>=' },
orFields: ['name', 'email'] // WHERE (name = ? OR email = ?)
}Pass comma-separated values for an IN clause:
GET /users?role=admin,manager
Pagination
Enable with pagination: true. Control via query params:
GET /users?page=2&limit=20
Joins
joins: [
{
type: 'LEFT', // 'INNER' | 'LEFT' | 'RIGHT'
table: 'roles',
condition: 'users.role_id = roles.id'
}
]Response Format
All responses follow a consistent structure.
Success:
{
"success": true,
"data": { ... },
"timestamp": "2026-06-22T10:00:00.000Z"
}Error:
{
"success": false,
"error": {
"message": "Duplicate entry",
"code": 409,
"details": null
},
"timestamp": "2026-06-22T10:00:00.000Z"
}DAB automatically maps common database error codes (duplicate entry, foreign key violation, null constraint, missing table) to appropriate HTTP status codes.
Advanced Usage
Attaching middleware
Pass a middleware option to dabApi() to run middleware before every registered route:
const { router } = dabApi(config, {
middleware: [authMiddleware, rateLimitMiddleware]
});Accessing sub-modules
dabApi() returns an object with all internal modules exposed:
const { router, validator, executor, queryBuilder, hooks, response } = dabApi(config);Running raw queries
const { queryBuilder } = dabApi(config);
await queryBuilder.initPool();
const rows = await queryBuilder.execute('SELECT * FROM users WHERE id = ?', [1]);Transactions
await queryBuilder.beginTransaction();
try {
await queryBuilder.execute('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, 1]);
await queryBuilder.execute('UPDATE accounts SET balance = balance + ? WHERE id = ?', [100, 2]);
await queryBuilder.commitTransaction();
} catch (err) {
await queryBuilder.rollbackTransaction();
throw err;
}API Reference
dabApi(config, options?)
Main entry point. Returns { router, validator, executor, queryBuilder, hooks, response }.
| Param | Type | Description |
|---|---|---|
config.database |
object |
DB connection config (optional if pool already configured) |
config.apis |
array |
Array of API definitions (required) |
options.middleware |
fn|fn[] |
Middleware to run before every route |
DabQueryBuilder
| Method | Description |
|---|---|
configure(config) |
Set DB config and initialize pool |
initPool() |
Open and verify the connection pool |
execute(sql, params) |
Run a raw parameterized query |
beginTransaction() |
Start a DB transaction |
commitTransaction() |
Commit the active transaction |
rollbackTransaction() |
Roll back the active transaction |
closePool() |
Drain and close the pool |
DabHooks
| Method | Description |
|---|---|
register(name, fn) |
Register a hook function |
registerBatch(obj) |
Register multiple hooks |
execute(name, req, data?) |
Execute a registered hook |
has(name) |
Check if a hook is registered |
remove(name) |
Unregister a hook |
Author
Gulshan Marandi
License
ISC