New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@nam088/json-logic-to-sql

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nam088/json-logic-to-sql

A powerful, secure, and type-safe library to convert JSON Logic rules into SQL WHERE clauses with comprehensive schema validation and multi-dialect support

latest
Source
npmnpm
Version
1.16.1
Version published
Maintainers
1
Created
Source

@nam088/json-logic-to-sql

License TypeScript

A powerful, secure, and type-safe library to convert JSON Logic rules into SQL WHERE clauses with comprehensive schema validation and multi-dialect support.

Features

  • Secure by Default: Generates parameterized SQL queries to prevent SQL injection attacks
  • Multi-Dialect Support: Works with PostgreSQL, MySQL, MSSQL (SQL Server), and SQLite
  • Type-Safe: Written in TypeScript with comprehensive type definitions and strict validation
  • Schema Validation: Validates rules against a defined schema before compilation with customizable constraints
  • Advanced Field Mapping: Support for column mapping, JSONB paths, and computed fields
  • Field Transformations: Built-in SQL transforms (LOWER, UPPER, TRIM, DATE functions, etc.)
  • Flexible Operators: Comprehensive operator support including comparison, logical, string, array, and JSONB operations
  • Query Utilities: Built-in helpers for pagination, sorting, and SELECT clause generation
  • Extensible: Support for custom transforms and field constraints

Installation

npm install @nam088/json-logic-to-sql

Quick Start

1. Define your Schema

Define the fields that are allowed to be queried with their types, operators, and optional constraints.

import { FilterSchema } from '@nam088/json-logic-to-sql';

const schema: FilterSchema = {
  fields: {
    firstName: {
      type: 'string',
      operators: ['eq', 'ne', 'like', 'in'],
      column: 'first_name', // Maps 'firstName' to 'first_name' column
    },
    age: {
      type: 'number',
      operators: ['eq', 'gt', 'lt', 'gte', 'lte', 'between'],
      constraints: {
        min: 0,
        max: 150,
      },
    },
    status: {
      type: 'string',
      operators: ['eq', 'in'],
      options: {
        items: [
          { value: 'active', label: 'Active' },
          { value: 'inactive', label: 'Inactive' },
          { value: 'pending', label: 'Pending' },
        ],
        strict: true, // Only allow predefined values
      },
    },
    email: {
      type: 'string',
      operators: ['eq', 'like', 'ilike'],
      constraints: {
        pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
      },
    },
  },
  settings: {
    maxDepth: 5,
    maxConditions: 100,
  },
};

2. Initialize the Compiler

import { JsonLogicCompiler } from '@nam088/json-logic-to-sql';

const compiler = new JsonLogicCompiler({
  schema,
  dialect: 'postgresql', // or 'mysql', 'mssql', 'sqlite'
});

3. Compile Rules

const rule = {
  and: [
    { '==': [{ var: 'status' }, 'active'] },
    { '>': [{ var: 'age' }, 18] }
  ]
};

try {
  const result = compiler.compile(rule);
  console.log(result.sql);
  // Output: (status = $1 AND age > $2)
  
  console.log(result.params);
  // Output: { '$1': 'active', '$2': 18 }
} catch (error) {
  console.error('Compilation failed:', error.message);
}

Core Concepts

Field Schema

Each field in your schema can have:

{
  type: FieldType;              // Data type (string, number, boolean, date, array, etc.)
  operators: Operator[];        // Allowed operators for this field
  column?: string;              // Database column name (if different from field name)
  filterable?: boolean;         // Allow in WHERE clauses (default: true)
  selectable?: boolean;         // Allow in SELECT clauses (default: true)
  sortable?: boolean;           // Allow in ORDER BY (default: true)
  nullable?: boolean;           // Allow null values
  caseSensitive?: boolean;      // For string comparisons
  options?: OptionConfig;       // Predefined values
  constraints?: FieldConstraints; // Validation rules
  transform?: FieldTransform;   // SQL transformations
  jsonPath?: string;            // JSONB path for nested data
  meta?: Record<string, unknown>; // Custom metadata
}

Computed Fields

Define fields that are calculated from SQL expressions:

const schema: FilterSchema = {
  fields: {
    fullName: {
      type: 'string',
      operators: ['like', 'ilike'],
      computed: true,
      expression: "first_name || ' ' || last_name",
    },
    ageGroup: {
      type: 'string',
      operators: ['eq', 'in'],
      computed: true,
      expression: "CASE WHEN age < 18 THEN 'minor' ELSE 'adult' END",
    },
  },
};

Field Transformations

Apply SQL transformations to fields:

const schema: FilterSchema = {
  fields: {
    email: {
      type: 'string',
      operators: ['eq', 'like'],
      transform: {
        input: 'lower', // Apply LOWER() when filtering
        output: 'lower', // Apply LOWER() when selecting
      },
    },
    createdAt: {
      type: 'datetime',
      operators: ['eq', 'gt', 'lt'],
      transform: {
        input: 'date', // Extract date part for comparison
      },
    },
  },
};

Built-in transforms: lower, upper, trim, ltrim, rtrim, unaccent, date, year, month, day

Custom transforms:

transform: {
  input: {
    name: 'custom',
    sql: 'CUSTOM_FUNCTION({column})'
  }
}

Field Constraints

Validate values before compilation:

constraints: {
  // String constraints
  minLength: 3,
  maxLength: 100,
  pattern: /^[a-zA-Z]+$/,
  
  // Number constraints
  min: 0,
  max: 1000,
  
  // Array constraints
  minItems: 1,
  maxItems: 10,
  
  // Date constraints
  dateFormat: 'YYYY-MM-DD',
  minDate: '2020-01-01',
  maxDate: '2025-12-31',
  
  // Custom validation
  validate: (value) => {
    if (typeof value === 'string' && value.includes('admin')) {
      return 'Cannot filter by admin values';
    }
    return true;
  }
}

Supported Operators

Comparison Operators

JSON LogicInternalSQLDescription
==, ===eq=Equal
!=, !==ne<>Not equal
>gt>Greater than
>=gte>=Greater than or equal
<lt<Less than
<=lte<=Less than or equal

Logical Operators

  • and - Logical AND
  • or - Logical OR
  • !, not - Logical NOT

String Operators

OperatorDescriptionExample
likeSQL LIKE (case-sensitive){ like: [{ var: 'name' }, 'John%'] }
ilikeCase-insensitive LIKE (PostgreSQL){ ilike: [{ var: 'name' }, 'john%'] }
starts_withStarts with string{ starts_with: [{ var: 'name' }, 'John'] }
ends_withEnds with string{ ends_with: [{ var: 'email' }, '.com'] }
containsContains substring{ contains: [{ var: 'description' }, 'urgent'] }
regexRegular expression match{ regex: [{ var: 'code' }, '^[A-Z]{3}'] }

Array Operators

OperatorDescriptionExample
inValue in list{ in: [{ var: 'status' }, ['active', 'pending']] }
not_inValue not in list{ not_in: [{ var: 'status' }, ['deleted']] }
betweenValue between two values{ between: [{ var: 'age' }, 18, 65] }
not_betweenValue not between{ not_between: [{ var: 'price' }, 100, 200] }

Array Column Operators (PostgreSQL)

For fields with type: 'array':

OperatorDescriptionExample
containsArray contains values{ contains: [{ var: 'tags' }, ['urgent', 'bug']] }
contained_byArray contained by values{ contained_by: [{ var: 'tags' }, ['all', 'possible', 'tags']] }
overlapsArrays overlap{ overlaps: [{ var: 'categories' }, ['tech', 'news']] }
any_ofValue equals any element{ any_of: [{ var: 'tags' }, 'urgent'] }
not_any_ofValue not in array{ not_any_of: [{ var: 'tags' }, 'spam'] }

JSONB Operators (PostgreSQL)

OperatorDescriptionExample
json_containsJSONB contains{ json_contains: [{ var: 'metadata' }, {"key": "value"}] }
json_has_keyJSONB has key{ json_has_key: [{ var: 'metadata' }, 'priority'] }
json_has_any_keysJSONB has any keys{ json_has_any_keys: [{ var: 'metadata' }, ['key1', 'key2']] }

Null Check Operators

OperatorDescriptionExample
is_nullValue is NULL{ is_null: [{ var: 'deletedAt' }] }
is_not_nullValue is not NULL{ is_not_null: [{ var: 'email' }] }

Database Dialect Support

DialectKeyPlaceholder StyleNotes
PostgreSQLpostgresql$1, $2, $3, ...Full feature support including ILIKE, array operators, JSONB
MySQLmysql?, ?, ?, ...Standard SQL features
SQL Servermssql@p1, @p2, @p3, ...Standard SQL features
SQLitesqlite?, ?, ?, ...Standard SQL features

Advanced Features

Schema Mapping Utilities

Separate public schema (for frontend) from internal database schema:

import { applyFieldMappings, toPublicSchema } from '@nam088/json-logic-to-sql';

// Define public schema (clean, no DB details)
const publicSchema: FilterSchema = {
  fields: {
    userName: { type: 'string', operators: ['eq', 'like'] },
    userEmail: { type: 'string', operators: ['eq'] },
    tags: { type: 'array', operators: ['contains'] },
    priority: { type: 'string', operators: ['eq'] },
  },
};

// Apply internal mappings on backend
const internalSchema = applyFieldMappings(publicSchema, {
  columns: {
    userName: { table: 'users', column: 'name' },
    userEmail: { table: 'users', column: 'email' },
    tags: '_tags', // Simple column rename
  },
  jsonPaths: {
    priority: "metadata->>'priority'", // JSONB path
  },
});

// Send public schema to frontend (without internal details)
const schemaForFrontend = toPublicSchema(internalSchema);

Pagination and Sorting

import { buildPagination, buildSort } from '@nam088/json-logic-to-sql';

// Build pagination
const pagination = buildPagination(
  { page: 2, pageSize: 20 },
  100, // max page size
  1    // starting param index
);
console.log(pagination.sql);
// Output: LIMIT $1 OFFSET $2
console.log(pagination.params);
// Output: { '$1': 20, '$2': 20 }

// Build sorting
const sort = buildSort(
  [
    { field: 'createdAt', direction: 'desc' },
    { field: 'name', direction: 'asc' },
  ],
  schema
);
console.log(sort.sql);
// Output: ORDER BY "created_at" DESC, "name" ASC

SELECT Clause Generation

import { buildSelect } from '@nam088/json-logic-to-sql';

const select = buildSelect(schema, {
  fields: ['firstName', 'lastName', 'email'],
  exclude: ['password'],
});
console.log(select.sql);
// Output: "first_name" AS "firstName", "last_name" AS "lastName", "email"

Complete Query Example

const compiler = new JsonLogicCompiler({ schema, dialect: 'postgresql' });

// Compile WHERE clause
const where = compiler.compile({
  and: [
    { '==': [{ var: 'status' }, 'active'] },
    { '>': [{ var: 'age' }, 18] }
  ]
});

// Build SELECT
const select = buildSelect(schema, {
  fields: ['firstName', 'lastName', 'email', 'age']
});

// Build ORDER BY
const sort = buildSort([{ field: 'lastName', direction: 'asc' }], schema);

// Build LIMIT/OFFSET
const pagination = buildPagination({ page: 1, pageSize: 20 });

// Combine into full query
const query = `
  SELECT ${select.sql}
  FROM users
  WHERE ${where.sql}
  ${sort.sql}
  ${pagination.sql}
`;

const allParams = { ...where.params, ...pagination.params };

Error Handling

The library throws specific error types for different scenarios:

import { 
  CompilerError, 
  SchemaValidationError 
} from '@nam088/json-logic-to-sql';

try {
  const result = compiler.compile(rule);
} catch (error) {
  if (error instanceof SchemaValidationError) {
    // Schema validation failed
    console.error('Field:', error.field);
    console.error('Operator:', error.operator);
    console.error('Message:', error.message);
  } else if (error instanceof CompilerError) {
    // Compilation error (invalid rule structure, etc.)
    console.error('Compilation error:', error.message);
  }
}

Security

The library implements multiple security layers:

  • Parameterized Queries: All values are passed as parameters, never interpolated
  • Identifier Escaping: Column and table names are properly quoted
  • Schema Validation: Only allowed fields and operators can be used
  • Input Sanitization: Deep sanitization of input rules
  • Constraint Validation: Values are validated against defined constraints
  • Depth Limiting: Prevents deeply nested rules that could cause performance issues
  • Condition Limiting: Prevents queries with excessive conditions

TypeScript Support

Full TypeScript support with comprehensive type definitions:

import type {
  FilterSchema,
  FieldSchema,
  ComputedFieldSchema,
  CompilerConfig,
  SqlResult,
  Operator,
  FieldType,
  FieldConstraints,
  FieldTransform,
  TransformFn,
  CustomTransform,
  OptionConfig,
  OptionItem,
  LookupRegistry,
  LookupConfig,
} from '@nam088/json-logic-to-sql';

Examples

479: 480: For more comprehensive examples, check out the examples directory or the unit tests which cover all supported scenarios.

Complex Nested Query

const rule = {
  or: [
    {
      and: [
        { '==': [{ var: 'status' }, 'active'] },
        { '>': [{ var: 'age' }, 18] },
        { 'in': [{ var: 'country' }, ['US', 'CA', 'UK']] }
      ]
    },
    {
      and: [
        { '==': [{ var: 'role' }, 'admin'] },
        { 'is_not_null': [{ var: 'lastLogin' }] }
      ]
    }
  ]
};

const result = compiler.compile(rule);
// Generates: ((status = $1 AND age > $2 AND country IN ($3, $4, $5)) OR (role = $6 AND last_login IS NOT NULL))

Array Operations (PostgreSQL)

const schema: FilterSchema = {
  fields: {
    tags: {
      type: 'array',
      operators: ['contains', 'overlaps', 'any_of'],
    },
  },
};

// Check if array contains all values
const rule1 = { contains: [{ var: 'tags' }, ['urgent', 'bug']] };
// SQL: tags @> $1  (where $1 = ['urgent', 'bug'])

// Check if arrays overlap
const rule2 = { overlaps: [{ var: 'tags' }, ['tech', 'news']] };
// SQL: tags && $1

// Check if value is in array column
const rule3 = { any_of: [{ var: 'tags' }, 'urgent'] };
// SQL: $1 = ANY(tags)

JSONB Queries (PostgreSQL)

const schema: FilterSchema = {
  fields: {
    metadata: {
      type: 'jsonb',
      operators: ['json_contains', 'json_has_key'],
    },
    priority: {
      type: 'string',
      operators: ['eq', 'in'],
      jsonPath: "metadata->>'priority'",
    },
  },
};

const rule = {
  and: [
    { json_has_key: [{ var: 'metadata' }, 'priority'] },
    { '==': [{ var: 'priority' }, 'high'] }
  ]
};

Contributing

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

License

MIT

Author

@nam088

Keywords

json-logic

FAQs

Package last updated on 09 Jan 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts