
Security News
The Nightmare Before Deployment
Season’s greetings from Socket, and here’s to a calm end of year: clean dependencies, boring pipelines, no surprises.
json-expressions
Advanced tools
A JavaScript expression engine for JSON-based dynamic computations and function composition
A powerful JavaScript expression engine for JSON-based dynamic computations and function composition. JSON Expressions provides a declarative syntax for creating complex logic, transformations, and calculations that can be serialized, stored, and executed safely.
Perfect for configuration-driven applications, business rules engines, and anywhere you need dynamic logic represented as data.
JSON Expressions is built around several key principles:
$pipeeval()JSON Expressions is optimized for flexibility over raw execution speed. Expect:
Expressions are JSON objects that describe computations to be performed. Each expression has a single key that identifies the expression type (prefixed with $) and a value that provides the parameters:
Simple comparison:
{
$gt: 18;
}
Logical expressions:
{
$and: [{ $gte: 18 }, { $lt: 65 }];
}
Conditional logic:
{
$case: {
value: { $get: "status" },
cases: [{ when: "active", then: { $get: "fullName" } }],
default: "Inactive user"
}
}
Data transformation:
{
$pipe: [
{ $get: "children" },
{ $filterBy: { age: { $gte: 4 } } },
{ $map: { $get: "name" } },
];
}
JSON Expressions provides two distinct execution modes that serve different purposes:
apply(expression, inputData)Applies the expression to input data. The expression acts like a function that receives the input data and transforms or tests it.
import { createExpressionEngine } from "json-expressions";
// Create an engine (users choose their own packs)
const engine = createExpressionEngine();
// Expression: "is the input greater than 18?"
const expression = { $gt: 18 };
const inputData = 25;
const result = engine.apply(expression, inputData);
// Returns: true (because 25 > 18)
In apply mode, expressions receive the input data as context and operate on it:
{ $get: "name" } gets the "name" property from input data{ $identity: null } returns the input data unchanged (identity function){ $gt: 18 } tests if input data is greater than 18{ $filter: { $gte: 4 } } filters input array for items >= 4evaluate(expression)Evaluates the expression independently without input data. The expression is fully self-contained and produces a static result.
// Expression: "what is the sum of these numbers?"
const expression = { $sum: [1, 2, 3, 4] };
const result = engine.evaluate(expression);
// Returns: 10 (static calculation)
In evaluate mode, expressions contain all the data they need:
{ $get: { object: { "name": "Hadley" }, path: "name" } } gets property from the provided object{ $identity: "value" } returns "value" unchanged{ $gt: [25, 18] } tests if 25 > 18{ $sum: [1, 2, 3] } calculates sum of the provided numbersWhen to use which:
Use apply when you have input data and want to transform, filter, or test it
engine.apply(expression, userData)engine.apply(rule, customerData)engine.apply(validator, formData)Use evaluate when you need static calculations or have all data in the expression
engine.evaluate({ $sum: [10, 20, 30] })engine.evaluate({ $gt: [userAge, minimumAge] })engine.evaluate({ $get: { object: config, path: "apiUrl" } })Rule of thumb: If your expression needs external input data, use apply. If it's self-contained, use evaluate. If still in doubt, try to use the evaluate form first. If you run into a wall, you probably need to switch over to apply.
| Expression | Apply Mode | Evaluate Mode |
|---|---|---|
$gt | { $gt: 18 } | { $gt: [25, 18] } |
$get | { $get: "name" } | { $get: { object, path: "name" } } |
$identity | { $identity: null } | { $identity: "value" } |
$sum | { $sum: null } | { $sum: [1, 2, 3] } |
$mean | { $mean: null } | { $mean: [85, 90, 88] } |
Key difference: Apply mode expressions operate on the input data, while evaluate mode expressions contain all the data they need.
Apply Mode - Operating on input data:
const data = { scores: [85, 90, 88], threshold: 80 };
// Get a property from input
engine.apply({ $get: "scores" }, data);
// Returns: [85, 90, 88]
// Test input against threshold
engine.apply({ $gt: 80 }, 85);
// Returns: true
// Calculate mean of input array
engine.apply({ $mean: null }, [85, 90, 88]);
// Returns: 87.67
Evaluate Mode - Self-contained expressions:
// Get property from provided object
engine.evaluate({ $get: { object: { name: "Hadley" }, path: "name" } });
// Returns: "Hadley"
// Compare two provided values
engine.evaluate({ $gt: [85, 80] });
// Returns: true
// Calculate mean of provided array
engine.evaluate({ $mean: [85, 90, 88] });
// Returns: 87.67
// Wrong: Using apply syntax in evaluate mode
engine.evaluate({ $gt: 18 }); // Error: needs comparison values
// Correct: Provide both values in evaluate mode
engine.evaluate({ $gt: [25, 18] }); // Returns: true
// Wrong: Using evaluate syntax in apply mode
engine.apply({ $gt: [25, 18] }, inputData); // Ignores inputData
// Correct: Test inputData against threshold
engine.apply({ $gt: 18 }, 25); // Returns: true
The dual-mode architecture enables the same expressions to run consistently across different environments:
// Same rule logic used in multiple contexts
const eligibilityRule = {
$matches: {
age: { $gte: 18 },
status: { $eq: "active" },
balance: { $gt: 0 },
},
};
// Frontend: Preview rule results
const previewResult = frontendEngine.apply(eligibilityRule, userData);
// Backend: Enforce rule in production
const productionResult = backendEngine.apply(eligibilityRule, userData);
// Database: Store rule for later execution
await database.saveRule("user-eligibility", eligibilityRule);
Because expressions are pure JSON, they can be:
Different contexts can use different expression engines with tailored capabilities:
// Restrictive engine for user-facing rule builders
const userEngine = createExpressionEngine({ packs: [filtering] });
// Full-featured engine for admin interfaces
const adminEngine = createExpressionEngine({
packs: [filtering, projection, math],
});
// Specialized engine for specific domains
const geoEngine = createExpressionEngine({
packs: [filtering],
custom: { $withinRadius, $intersects, $contains },
});
JSON Expressions powers production systems including:
Get up and running in 30 seconds:
import { createExpressionEngine } from "json-expressions";
// Create engine - users choose packs they need
const engine = createExpressionEngine();
// Data filtering
const children = [
{ name: "Chen", age: 3 },
{ name: "Amara", age: 5 },
{ name: "Diego", age: 4 },
];
// Find school-age children (5+)
const schoolAge = engine.apply(
{
$pipe: [
{ $filter: { $matches: { age: { $gte: 5 } } } },
{ $map: { $get: "name" } },
],
},
children,
);
// Returns: ["Amara"]
Users create engines with the packs they need:
import { createExpressionEngine } from "json-expressions";
const engine = createExpressionEngine(); // Includes base pack by default
const result = engine.apply(expression, data);
const staticResult = engine.evaluate(expression);
createExpressionEngine(config)Creates a custom expression engine with specified configuration.
Parameters:
config.packs (Array, optional) - Array of expression pack objects to includeconfig.custom (Object, optional) - Custom expression definitionsconfig.includeBase (Boolean, default: true) - Whether to include base expressions. Note that $literal cannot be excluded or overwritten.import { createExpressionEngine } from "json-expressions";
import { math } from "json-expressions/packs/math";
// Create engine with math pack
const mathEngine = createExpressionEngine({
packs: [math],
});
// Create engine with only custom expressions (no base pack)
const customEngine = createExpressionEngine({
includeBase: false,
custom: {
$double: {
apply: (operand, inputData) => inputData * 2,
evaluate: (operand) => operand * 2,
},
},
});
apply(expression, inputData)Evaluates an expression against input data.
evaluate(expression)Evaluates an expression without input data (for static expressions).
isExpression(value)Tests whether a value is a valid expression.
expressionNamesArray of all available expression names in the engine.
JSON Expressions provides clear error messages for common issues:
// Unknown expression
engine.apply({ $unknown: 5 }, 10);
// Error: Unknown expression operator: "$unknown". Did you mean "$not"?
// Values that look like expressions but aren't
engine.apply({ $notAnExpression: 5 }, 10);
// Error: Unknown expression operator: "$notAnExpression". Use { $literal: {"$notAnExpression": 5} } if you meant this as a literal value.
// Wrong operand type
engine.apply({ $get: 123 }, { name: "Chen" });
// Error: $get operand must be string
// Boolean predicates must return boolean values
engine.apply({
$case: {
value: 5,
cases: [{ when: { $literal: "not boolean" }, then: "result" }],
},
});
// Works fine - literal comparison: 5 === "not boolean" → false, continues to default
// Strict property access on null/undefined
engine.apply({ $prop: "name" }, null);
// Error: Cannot read properties of null (reading 'name')
// Missing required properties
engine.evaluate({ $get: { path: "name" } });
// Error: $get evaluate form requires 'object' and 'path' properties
$get with defaults for safe property access: { $get: "name" }$literal for values that might be confused with expressions$debug (available via direct import) to inspect intermediate values in complex pipelinesJSON Expressions organizes functionality into packs - curated collections of expressions for specific use cases.
The base pack contains near-universal expressions used across almost all scenarios. These expressions are included by default in every engine unless explicitly excluded.
Comparison expressions:
Beyond the base pack, you can import additional functionality as needed:
import { createExpressionEngine, math, string } from "json-expressions";
const engine = createExpressionEngine({
packs: [math, string],
});
Statistical and aggregation functions for data analysis:
Complete array manipulation toolkit:
Scalar comparison operations for filtering and validation:
Complete toolkit for WHERE clause logic and data filtering - combines field access, comparisons, logic, and pattern matching:
Perfect for building complex filters with a single import:
import { createExpressionEngine, filtering } from "json-expressions";
const engine = createExpressionEngine({ packs: [filtering] });
// Complex daycare filtering
const activeToddlers = engine.apply(
{
$matches: {
age: { $and: [{ $gte: 2 }, { $lte: 4 }] }, // Age between 2 and 4
status: { $eq: "active" }, // Active status
activity: { $nin: ["napping", "sick"] }, // Not napping or sick
},
},
children,
);
Boolean logic and conditional operations:
Complete toolkit for SELECT clause operations and data transformation - combines aggregation, array operations, string transforms, and conditionals:
Perfect for transforming and projecting data with a single import:
import { createExpressionEngine, projection } from "json-expressions";
const engine = createExpressionEngine({ packs: [projection] });
// Complex daycare reporting
const report = engine.apply(
{
$pipe: [
{ $get: "children" },
{ $filter: { $get: "active" } },
{
$map: {
name: { $get: "name" },
displayName: { $uppercase: { $get: "name" } },
ageGroup: {
$if: {
if: { $matches: { age: { $gte: 4 } } },
then: "Pre-K",
else: "Toddler",
},
},
activities: { $join: ", " },
},
},
],
},
daycareData,
);
Key-value manipulation and object operations:
Arithmetic operations and mathematical functions:
String processing and pattern matching:
Some expressions are available for direct import when you need them outside of packs:
The $debug expression is useful for development and troubleshooting but isn't included in any pack by default. Import it directly when needed:
import { createExpressionEngine } from "json-expressions";
import { $debug } from "json-expressions/src/definitions/flow";
const engine = createExpressionEngine({
custom: { $debug },
});
// Use in pipelines to inspect intermediate values
const result = engine.apply(
{
$pipe: [
{ $get: "users" },
{ $debug: "After getting users" }, // Logs to console
{ $filter: { active: true } },
{ $debug: "After filtering active" }, // Logs to console
{ $map: { $get: "name" } },
],
},
data,
);
Any expression can be included with this method. Use it if you don't want the overhead of an entire pack.
JSON Expressions excel at composing simple operations into complex logic:
// Start simple: basic comparison
{
$gt: 5;
}
// Add logic: combine conditions
{
$and: [{ $gt: 5 }, { $lt: 10 }];
}
// Add data access: work with objects
{
$pipe: [{ $get: "age" }, { $and: [{ $gt: 5 }, { $lt: 10 }] }];
}
// Add transformation: complex pipeline
{
$pipe: [
{ $filter: { $gte: 4 } }, // Filter items >= 4
{ $map: { $multiply: 2 } }, // Double each value
{ $sum: null }, // Sum the results
];
}
// Input: [1, 2, 4, 6, 8] → Output: 36
// Conditional values
{
$if: {
if: { $matches: { age: { $gt: 18 } } },
then: "adult",
else: "minor",
}
}
// Complex filtering with multiple conditions
{
$filter: {
$and: [
{ $get: "active" },
{ $pipe: [{ $get: "age" }, { $gte: 18 }] },
]
}
}
// Simplified filtering by object properties
{ $filterBy: { active: { $eq: true }, age: { $gte: 18 } } }
// Nested data transformation
{
$map: {
name: { $get: "name" },
isEligible: { $pipe: [{ $get: "score" }, { $gte: 75 }] },
category: {
$case: {
value: { $get: "age" },
cases: [
{ when: { $lt: 13 }, then: "child" },
{ when: { $lt: 20 }, then: "teen" },
],
default: "adult"
}
}
}
}
import { createExpressionEngine } from "json-expressions";
const engine = createExpressionEngine();
const daycareData = {
teacher: { name: "James", age: 46 },
children: [
{ name: "Chen", age: 4, activity: "playing" },
{ name: "Serafina", age: 5, activity: "reading" },
{ name: "Diego", age: 3, activity: "napping" },
],
};
// Get teacher name
const teacherName = engine.apply({ $get: "teacher.name" }, daycareData);
// Returns: "James"
// Find children ready for kindergarten (age 5+)
const kindergartenReady = engine.apply(
{
$pipe: [
{ $get: "children" },
{ $filter: { $gte: 5 } },
{ $map: { $get: "name" } },
],
},
daycareData,
);
// Returns: ["Serafina"]
// Calculate meal budget
const totalMealCost = engine.evaluate({
$sum: [8.5, 12.75, 4.25], // breakfast, lunch, snack
});
// Returns: 25.5
// Age-based activity recommendations with mixed literal and expression conditions
const activityRecommendation = engine.apply(
{
$case: {
value: { $get: "age" },
cases: [
{ when: 2, then: "Sensory play and simple puzzles" }, // Literal comparison
{ when: 3, then: "Art activities and story time" }, // Literal comparison
{ when: { $eq: 4 }, then: "Pre-writing skills and group games" }, // Expression predicate
{ when: { $gte: 5 }, then: "Early math and reading readiness" }, // Expression predicate
],
default: "Age-appropriate developmental activities",
},
},
{ age: 4 },
);
// Returns: "Pre-writing skills and group games"
You can extend JSON Expressions with custom functionality. For comprehensive documentation on creating custom expressions, see Custom Expressions Guide.
import { createExpressionEngine } from "json-expressions";
const customEngine = createExpressionEngine({
custom: {
// Custom validation
$isValidEmail: {
apply: (operand, inputData) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(inputData);
},
evaluate: (operand) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(operand);
},
},
// Custom transformation
$titleCase: {
apply: (operand, inputData) => {
return inputData
.toLowerCase()
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
},
evaluate: (operand) => {
return operand
.toLowerCase()
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
},
},
},
});
// Use custom expressions
const isValid = customEngine.apply({ $isValidEmail: null }, "user@example.com");
// Returns: true
const formatted = customEngine.evaluate({ $titleCase: "john doe" });
// Returns: "John Doe"
import { createExpressionEngine } from "json-expressions";
import { object } from "json-expressions/packs/object";
const engine = createExpressionEngine({ packs: [object] });
const students = [
{ name: "Aisha", age: 3, scores: [85, 90, 88], active: true },
{ name: "Chen", age: 5, scores: [92, 87, 95], active: true },
{ name: "Diego", age: 3, scores: [78, 84, 91], active: false },
{ name: "Serafina", age: 6, scores: [88, 92, 85], active: true },
];
// Use $filterBy for elegant filtering
const activeOlderStudents = engine.apply(
{
$filterBy: {
active: { $eq: true },
age: { $lt: 6 },
},
},
students,
);
// Returns: ["Aisha", "Chen"]
// Use $pluck to extract specific fields
const studentNames = engine.apply({ $pluck: "name" }, students);
// Returns: ["Aisha", "Chen", "Diego", "Serafina"]
// Use $groupBy to organize data
const studentsByAge = engine.apply({ $groupBy: "age" }, students);
// Returns: {
// "3": [{ name: "Aisha", age: 3, scores: [85, 90, 88], active: true }, { name: "Diego", age: 3, scores: [78, 84, 91], active: false }],
// "5": [{ name: "Chen", age: 5, scores: [92, 87, 95], active: true }],
// "6": [{ name: "Serafina", age: 6, scores: [88, 92, 85], active: true }]
// }
// Use $select to project/transform objects
const summaries = engine.apply(
{
$map: {
$select: {
name: { $get: "name" },
averageScore: { $pipe: [{ $get: "scores" }, { $mean: null }] },
isActive: { $get: "active" },
},
},
},
students,
);
// Returns: [
// { name: "Aisha", averageScore: 87.67, isActive: true },
// { name: "Chen", averageScore: 91.33, isActive: true },
// ...
// ]
// Use $has to check for property existence
const hasScores = engine.apply({ $has: "scores" }, students[0]);
// Returns: true
// Use $flatten for nested arrays
const allScores = engine.apply(
{
$pipe: [{ $pluck: "scores" }, { $flatten: null }],
},
students,
);
// Returns: [85, 90, 88, 92, 87, 95, 78, 84, 91, 88, 92, 85]
// Use $unique to remove duplicates
const uniqueAges = engine.apply(
{
$pipe: [{ $pluck: "age" }, { $unique: null }],
},
students,
);
// Returns: [3, 5, 6]
JSON Expressions includes comprehensive TypeScript definitions for type safety and better developer experience.
import { createExpressionEngine, Expression } from "json-expressions";
const engine = createExpressionEngine();
// Type-safe expression definition
const expression: Expression = { $gt: 18 };
// Apply with typed input and output
const result: unknown = engine.apply(expression, 25);
// Type guards for expressions
if (engine.isExpression(someValue)) {
// someValue is now typed as Expression
const result = engine.apply(someValue, data);
}
import { createExpressionEngine, ExpressionEngine } from "json-expressions";
// Create typed engine
const engine: ExpressionEngine = createExpressionEngine({
custom: {
$myExpression: {
apply: (operand: string, inputData: any) => inputData + operand,
evaluate: (operand: string) => operand.toUpperCase(),
},
},
});
The library provides specific interfaces for each expression type:
import {
GetExpression,
PipeExpression,
FilterExpression,
} from "json-expressions";
const getExpr: GetExpression = { $get: "name" };
const pipeExpr: PipeExpression = {
$pipe: [{ $get: "children" }, { $filter: { $gte: 5 } }],
};
npm install json-expressions
MIT
FAQs
A JavaScript expression engine for JSON-based dynamic computations and function composition
The npm package json-expressions receives a total of 16 weekly downloads. As such, json-expressions popularity was classified as not popular.
We found that json-expressions demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

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.

Security News
Season’s greetings from Socket, and here’s to a calm end of year: clean dependencies, boring pipelines, no surprises.

Research
/Security News
Impostor NuGet package Tracer.Fody.NLog typosquats Tracer.Fody and its author, using homoglyph tricks, and exfiltrates Stratis wallet JSON/passwords to a Russian IP address.

Security News
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.