Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

dynamic-orm

waqasbelushi39MIT1.2.1TypeScript support: included

A flexible and powerful dynamic ORM for SQL databases with Redis caching support and many-to-many relationship handling

orm, database, sql, redis, caching, dynamic, many-to-many, relationships, junction, pivot-table

readme

Dynamic ORM

A flexible and powerful ORM for SQL databases with Redis caching support.

Features

  • Fluent query builder with support for complex filtering and pagination
  • Support for table relationships with automatic JOIN generation
  • Redis caching for improved performance
  • Transaction support
  • TypeScript support with full type definitions

Installation

npm install dynamic-orm

Basic Usage

const { createORM } = require('dynamic-orm');
const { prepare, transaction } = require('your-database-module');
const redis = require('your-redis-module');

// Create ORM instance
const orm = createORM({
  db: { prepare, transaction },  // Your database adapter
  redis: redis,                  // Your Redis client
  useCache: true                 // Enable caching
});

// Create a model for a specific table
const userModel = orm.createModel('users', {
  primaryKey: 'id',
  searchableFields: ['firstName', 'lastName', 'email'],
  defaultLimit: 50,
  maxLimit: 500
});

// Now you can use the model to interact with the database
async function example() {
  // Find all users with filtering, pagination, and sorting
  const result = await userModel.findAll({
    filters: { 
      role: 'admin',
      status: 'active',
      createdAt: { gt: '2023-01-01' }
    },
    sort: { lastName: 'asc', firstName: 'asc' },
    pagination: { page: 2, limit: 25 },
    fields: ['id', 'firstName', 'lastName', 'email'],
    search: 'john'
  });

  console.log(result.data); // Array of user objects
  console.log(result.pagination); // Pagination metadata

  // Find a user by ID
  const user = await userModel.findById('user-123');

  // Find a user by email
  const userByEmail = await userModel.findByField('email', 'user@example.com');

  // Create a new user
  const newUser = await userModel.create({
    firstName: 'John',
    lastName: 'Doe',
    email: 'john@example.com'
  });

  // Update a user
  const updatedUser = await userModel.update('user-123', {
    status: 'inactive'
  });

  // Delete a user
  await userModel.delete('user-123');

  // Count users
  const count = await userModel.count({ status: 'active' });
}

Advanced Usage

Relationships

You can fetch related data using the relations option:

const result = await userModel.findAll({
  filters: { status: 'active' },
  relations: [
    { 
      table: 'orders', 
      foreignKey: 'userId',
      localKey: 'id',
      as: 'orders',
      type: 'many', // or 'left', 'inner', 'right' for JOIN types
      select: ['id', 'total', 'createdAt'],
      filters: { status: 'completed' }
    }
  ]
});

// Result will include user records with their orders as nested objects
console.log(result.data);

Many-to-Many Relationships

For many-to-many relationships that use a junction table, use the through option:

// Example: Vehicles with their features through a junction table
const result = await vehicleModel.findAll({
  filters: { status: 'active' },
  relations: [
    {
      table: 'features',             // Target table (features)
      foreignKey: 'id',              // Primary key in the features table
      as: 'features',                // Property name in the result
      select: ['id', 'name'],        // Fields to select from features table
      through: 'vehicle_feature_links', // Junction table name
      throughLocalKey: 'stock_id',   // FK in junction table that references the main table (vehicles)
      throughForeignKey: 'feature_id'// FK in junction table that references the related table (features)
    }
  ]
});

// Each vehicle will have an array of features
console.log(result.data[0].features); // [{id: 1, name: 'GPS'}, {id: 2, name: 'Bluetooth'}, ...]

This approach handles the two joins required for many-to-many relationships and properly organizes the results, avoiding duplicates.

Simplified Arrays for Single Fields

When you select just a single field from a many-to-many relationship, the ORM automatically simplifies the result to an array of values rather than an array of objects:

// Example: Getting just feature names as a simple array
const result = await vehicleModel.findAll({
  filters: { status: 'active' },
  relations: [
    {
      table: 'features',
      foreignKey: 'id',
      as: 'features',
      select: ['name'],  // When selecting just one field, it returns a simplified array
      through: 'vehicle_feature_links',
      throughLocalKey: 'stock_id',
      throughForeignKey: 'feature_id'
    }
  ]
});

// Each vehicle will have features as a simple array of strings
console.log(result.data[0]); 
// Output:
// {
//   stock_id: 'ABC123',
//   make: 'Toyota',
//   model: 'Camry',
//   features: ['GPS Navigation', 'Bluetooth', 'Backup Camera']
// }

This makes it easier to work with lists of related values when you only need a single property.

Transactions

Execute multiple operations in a transaction:

const result = await orderModel.withTransaction(async (tr) => {
  // Create the order
  const order = await tr.create({
    customerId: 'cust-123',
    total: 99.99
  });

  // Add order items
  await tr.executeQuery(
    'INSERT INTO order_items (order_id, product_id, quantity) VALUES (?, ?, ?)',
    [order.id, 'prod-456', 2]
  );

  return order;
});

Custom Queries

Execute raw SQL queries when needed:

const results = await userModel.executeQuery(
  'SELECT id, email FROM users WHERE last_login > ? AND role = ?',
  ['2023-01-01', 'admin']
);

TypeScript Support

The package includes full TypeScript definitions:

import { createORM, Types } from 'dynamic-orm';

// Define your record type
interface User {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  role: string;
  status: string;
  createdAt: string;
}

// Create strongly typed model
const orm = createORM({ db, redis, useCache: true });
const userModel = orm.createModel<User>('users', {
  primaryKey: 'id',
  searchableFields: ['firstName', 'lastName', 'email']
});

// Now all queries will be properly typed
const result = await userModel.findAll({
  filters: { status: 'active' }
});

// result.data will be User[]
const users: User[] = result.data;

License

MIT