npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

flex-rules-engine

v1.0.23

Published

A flexible rules engine for TypeScript/JavaScript applications

Downloads

223

Readme

Flex Rules Engine

A flexible, powerful rules engine for TypeScript/JavaScript applications with built-in NestJS integration and MongoDB support.

Features

  • Flexible Rule Definition: Define complex business rules with nested conditions and multiple actions
  • Priority-Based Execution: Control rule execution order with priority values
  • Multiple Storage Options: MongoDB, in-memory, or custom storage adapters
  • NestJS Integration: First-class support for NestJS with decorators and dependency injection
  • REST API: Built-in CRUD endpoints with Swagger documentation
  • Rule Versioning: Track and revert rule changes
  • Bulk Operations: Create and update multiple rules at once
  • Type Safety: Full TypeScript support with comprehensive type definitions
  • Direct Service Integration: Inject the rules engine directly into your services for high-performance evaluation

Installation

npm install flex-rules-engine

Peer Dependencies

For NestJS integration with MongoDB:

npm install @nestjs/common @nestjs/core @nestjs/mongoose mongoose

Quick Start

NestJS Integration with MongoDB

// app.module.ts
import { Module } from '@nestjs/common';
import { RulesModule } from 'flex-rules-engine';

@Module({
  imports: [
    // Option 1: Use your existing MongoDB connection
    MongooseModule.forRoot('mongodb://localhost:27017/myapp'),
    RulesModule.forMongoDB({
      enableController: true,  // Enables REST API endpoints
      global: true,            // Makes the engine available globally
    }),

    // Option 2: Create a dedicated connection for rules
    RulesModule.forRootMongoDB({
      mongoUri: 'mongodb://localhost:27017',
      dbName: 'rulesdb',
      enableController: true,
      global: true,
    }),
  ],
})
export class AppModule {}

Auto-Loading Rules on Startup

// app.module.ts
import { Module, OnModuleInit, Inject, Logger } from '@nestjs/common';
import { FlexRulesEngine, RulesModule } from 'flex-rules-engine';

@Module({
  imports: [
    RulesModule.forRootMongoDB({
      mongoUri: 'mongodb://localhost:27017',
      dbName: 'rulesdb',
      enableController: true,
      global: true,
    }),
  ],
})
export class AppModule implements OnModuleInit {
  private readonly logger = new Logger(AppModule.name);

  constructor(
    @Inject('FLEX_RULES_ENGINE')
    private readonly rulesEngine: FlexRulesEngine,
  ) {}

  async onModuleInit() {
    try {
      this.logger.log('Loading rules from database...');
      await this.rulesEngine.loadRules();
      const loadedRules = this.rulesEngine.getRules();
      this.logger.log(`Rules Engine initialized with ${loadedRules.length} active rules`);

      if (loadedRules.length > 0) {
        loadedRules.forEach(rule => {
          this.logger.debug(`Loaded rule: ${rule.name} (priority: ${rule.priority})`);
        });
      } else {
        this.logger.warn('No active rules found in database');
      }
    } catch (error) {
      this.logger.error('Failed to load rules', error);
    }
  }
}

Usage

Direct Service Integration (Recommended)

Inject the rules engine directly into your services for maximum performance:

// your.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { FlexRulesEngine } from 'flex-rules-engine';

@Injectable()
export class YourService {
  constructor(
    @Inject('FLEX_RULES_ENGINE')
    private readonly rulesEngine: FlexRulesEngine,
  ) {}

  async processOrder(orderData: any) {
    // Evaluate rules against your data
    const result = this.rulesEngine.evaluate({
      data: {
        orderType: 'B2B',
        orderAmount: 5000,
        customerTier: 'Gold',
        ...orderData,
      }
    });

    if (result.success) {
      console.log('Applied rules:', result.appliedRules);
      console.log('Computed values:', result.results);

      // Use the computed values
      const discount = result.results.discount || 0;
      const priority = result.results.priority || 'normal';

      return {
        ...orderData,
        discount,
        priority,
        totalAmount: orderData.amount - discount,
      };
    } else {
      console.error('Rule evaluation errors:', result.errors);
      throw new Error('Failed to process order rules');
    }
  }

  async evaluateSpecificRules(data: any, ruleIds: string[]) {
    // Evaluate only specific rules by ID
    const result = this.rulesEngine.evaluate(
      { data },
      ruleIds
    );
    return result;
  }

  async getAvailableRules() {
    // Get all loaded rules
    const rules = this.rulesEngine.getRules();
    return rules.map(r => ({
      id: r._id,
      name: r.name,
      priority: r.priority,
      active: r.active,
    }));
  }
}

Creating Rules

Via REST API

POST /api/rules
Content-Type: application/json

{
  "name": "B2B Gold Customer Discount",
  "description": "Apply 10% discount for B2B Gold customers on orders over $1000",
  "priority": 100,
  "active": true,
  "conditions": {
    "operator": "AND",
    "rules": [
      {
        "field": "orderType",
        "operator": "equals",
        "value": "B2B"
      },
      {
        "field": "customerTier",
        "operator": "equals",
        "value": "Gold"
      },
      {
        "field": "orderAmount",
        "operator": "greaterThan",
        "value": 1000
      }
    ]
  },
  "actions": [
    {
      "type": "setValue",
      "target": "discount",
      "value": 0.10
    },
    {
      "type": "setValue",
      "target": "priority",
      "value": "high"
    },
    {
      "type": "calculate",
      "target": "finalAmount",
      "formula": "orderAmount * (1 - discount)"
    }
  ]
}

Programmatically

import { Injectable, Inject } from '@nestjs/common';
import { IRuleManager } from 'flex-rules-engine';

@Injectable()
export class RulesSetupService {
  constructor(
    @Inject('RULE_MANAGER')
    private readonly ruleManager: IRuleManager,
    @Inject('FLEX_RULES_ENGINE')
    private readonly rulesEngine: FlexRulesEngine,
  ) {}

  async setupInitialRules() {
    const rule = await this.ruleManager.createRule({
      name: 'B2B Gold Customer Discount',
      description: 'Apply 10% discount for B2B Gold customers',
      priority: 100,
      active: true,
      conditions: {
        operator: 'AND',
        rules: [
          { field: 'orderType', operator: 'equals', value: 'B2B' },
          { field: 'customerTier', operator: 'equals', value: 'Gold' },
          { field: 'orderAmount', operator: 'greaterThan', value: 1000 }
        ]
      },
      actions: [
        { type: 'setValue', target: 'discount', value: 0.10 },
        { type: 'setValue', target: 'priority', value: 'high' }
      ]
    });

    // Reload the engine to include the new rule
    await this.rulesEngine.reloadRules();

    return rule;
  }
}

Rule Definition

Rule Structure

interface RuleDefinition {
  _id?: string;              // Auto-generated MongoDB ID
  name: string;              // Human-readable name
  description?: string;      // Optional description
  priority: number;          // Execution order (higher = earlier)
  active: boolean;           // Enable/disable rule
  conditions: ConditionGroup; // When to apply this rule
  actions: Action[];         // What to do when conditions match
  metadata?: Record<string, any>; // Optional custom metadata
  version?: number;          // Auto-incremented version
  createdAt?: Date;
  updatedAt?: Date;
}

Condition Operators

Logical Operators

  • AND - All conditions must be true
  • OR - At least one condition must be true

Comparison Operators

Note: Both camelCase and snake_case naming conventions are supported.

Equality Operators:

  • equals - Exact match
  • notEquals / not_equals - Not equal

Numeric Comparison:

  • greaterThan / greater_than - Greater than
  • lessThan / less_than - Less than
  • greaterThanOrEqual / greater_than_or_equal - Greater than or equal
  • lessThanOrEqual / less_than_or_equal - Less than or equal

String Operators:

  • contains - String/array contains value
  • startsWith / starts_with - String starts with value
  • endsWith / ends_with - String ends with value
  • regex - Regex pattern match

Array Operators:

  • in - Value exists in array
  • notIn / not_in - Value does not exist in array

Operator Examples

// Equality
{ field: 'status', operator: 'equals', value: 'active' }
{ field: 'status', operator: 'notEquals', value: 'deleted' }

// Numeric comparison
{ field: 'age', operator: 'greaterThan', value: 18 }
{ field: 'price', operator: 'lessThanOrEqual', value: 100 }

// String operations
{ field: 'email', operator: 'contains', value: '@company.com' }
{ field: 'name', operator: 'startsWith', value: 'John' }
{ field: 'filename', operator: 'endsWith', value: '.pdf' }
{ field: 'email', operator: 'regex', value: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$' }

// Array operations
{ field: 'role', operator: 'in', value: ['admin', 'moderator'] }
{ field: 'status', operator: 'notIn', value: ['deleted', 'archived'] }

// Using snake_case (also works)
{ field: 'age', operator: 'greater_than', value: 18 }
{ field: 'email', operator: 'starts_with', value: 'admin' }

Action Types

setValue

Set a value in the results

{
  type: 'setValue',
  target: 'discount',
  value: 0.10
}

calculate

Perform mathematical calculations

{
  type: 'calculate',
  target: 'finalAmount',
  formula: 'orderAmount * (1 - discount)'
}

Supported operators: +, -, *, /, %, ^ (power)

transform

Transform a value using functions

{
  type: 'transform',
  target: 'email',
  field: 'rawEmail',
  function: 'toLowerCase'
}

Available functions: toUpperCase, toLowerCase, trim, round, floor, ceil, abs

merge

Merge objects or arrays

{
  type: 'merge',
  target: 'combinedData',
  sources: ['source1', 'source2']
}

REST API Endpoints

When enableController: true, the following endpoints are automatically available:

Rules Management

  • POST /api/rules - Create a rule
  • GET /api/rules - Get all rules (with filtering)
  • GET /api/rules/:id - Get rule by ID
  • PUT /api/rules/:id - Update a rule
  • DELETE /api/rules/:id - Delete a rule
  • POST /api/rules/:id/test - Test a rule with sample data
  • POST /api/rules/:id/validate - Validate rule structure
  • GET /api/rules/:id/versions - Get rule version history
  • POST /api/rules/:id/revert/:version - Revert to specific version

Bulk Operations

  • POST /api/rules/bulk/create - Create multiple rules
  • PUT /api/rules/bulk/update - Update multiple rules

Engine Operations

  • GET /api/rules/engine/status - Get engine status
  • POST /api/rules/engine/reload - Reload rules from database
  • POST /api/rules/engine/evaluate - Evaluate rules against data

Query Parameters

GET /api/rules?active=true&priority=100&search=discount&sortBy=priority&sortOrder=desc

API Response Format

All endpoints return responses in this format:

{
  "result": {
    // Single item or object
  }
}

// OR for multiple items

{
  "results": [
    // Array of items
  ]
}

Configuration Options

RulesModule.forMongoDB(options)

Uses an existing MongoDB connection:

{
  connectionString?: string;      // Optional: Override connection
  collectionName?: string;        // Optional: Custom collection name (default: 'rules')
  enableController?: boolean;     // Enable REST API (default: true)
  controllerPath?: string;        // Custom API path
  global?: boolean;               // Make module global (default: false)
}

RulesModule.forRootMongoDB(options)

Creates its own MongoDB connection:

{
  mongoUri: string;               // MongoDB connection URI
  dbName?: string;                // Database name
  collectionName?: string;        // Collection name (default: 'rules')
  enableController?: boolean;     // Enable REST API (default: true)
  controllerPath?: string;        // Custom API path
  global?: boolean;               // Make module global (default: false)
  mongoOptions?: Record<string, any>; // Additional Mongoose options
}

RulesModule.forMemory(options)

In-memory storage for development/testing:

{
  enableController?: boolean;     // Enable REST API (default: true)
  controllerPath?: string;        // Custom API path
  global?: boolean;               // Make module global (default: false)
}

Advanced Usage

Custom Rule Manager

import { IRuleManager } from 'flex-rules-engine';

class CustomRuleManager implements IRuleManager {
  // Implement your custom storage logic
}

@Module({
  imports: [
    RulesModule.forRoot({
      ruleManager: new CustomRuleManager(),
      enableController: true,
    }),
  ],
})
export class AppModule {}

Rule Evaluation Result

interface RuleExecutionResult {
  success: boolean;              // True if no errors occurred
  appliedRules: string[];        // IDs of rules that matched
  results: Record<string, any>;  // Computed values from actions
  errors?: Array<{               // Any errors that occurred
    ruleId: string;
    message: string;
    action?: string;
  }>;
  executionTime: number;         // Execution time in milliseconds
}

Examples

E-commerce Pricing

// Apply tiered discounts based on order value
{
  name: "Volume Discount",
  priority: 100,
  conditions: {
    operator: "AND",
    rules: [
      { field: "orderAmount", operator: "greaterThan", value: 10000 }
    ]
  },
  actions: [
    { type: "setValue", target: "discount", value: 0.15 },
    { type: "calculate", target: "finalAmount", formula: "orderAmount * 0.85" }
  ]
}

User Permissions

// Grant admin access for specific roles
{
  name: "Admin Access",
  priority: 200,
  conditions: {
    operator: "OR",
    rules: [
      { field: "role", operator: "equals", value: "admin" },
      { field: "role", operator: "equals", value: "superuser" }
    ]
  },
  actions: [
    { type: "setValue", target: "canAccessAdmin", value: true },
    { type: "setValue", target: "permissions", value: ["read", "write", "delete"] }
  ]
}

License

MIT

Author

yujuism

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.