SQL to MongoDB query translator for NodeJS
QueryLeaf is a library that translates SQL queries into MongoDB commands. It parses SQL using node-sql-parser, transforms it into an abstract command set, and then executes those commands against the MongoDB Node.js driver.
- Parse SQL statements into an abstract syntax tree using node-sql-parser
- Compile SQL AST into MongoDB commands
- Execute MongoDB commands using the official driver
- Support for basic SQL operations:
- SELECT
- INSERT
- UPDATE
- DELETE
- Advanced querying features:
- Nested field access (e.g.,
address.zip
) - Array element access (e.g.,
items[0].name
) - GROUP BY with aggregation functions (COUNT, SUM, AVG, MIN, MAX)
- JOINs between collections
- Nested field access (e.g.,
- Multiple interfaces:
- Library for direct integration in your code
- CLI for command-line SQL queries
- Web Server for REST API access
- PostgreSQL Wire Protocol Server for connecting with standard PostgreSQL clients
QueryLeaf translates SQL queries into MongoDB commands. Here are some examples of the translation:
SQL:
SELECT name, email FROM users WHERE age > 21
MongoDB:
db.collection('users').find(
{ age: { $gt: 21 } },
{ name: 1, email: 1 }
)
SQL:
SELECT name, address.city, address.zip FROM users WHERE address.city = 'New York'
MongoDB:
db.collection('users').find(
{ 'address.city': 'New York' },
{ name: 1, 'address.city': 1, 'address.zip': 1 }
)
SQL:
SELECT _id, items[0].name, items[0].price FROM orders WHERE items[0].price > 1000
MongoDB:
db.collection('orders').find(
{ 'items.0.price': { $gt: 1000 } },
{ _id: 1, 'items.0.name': 1, 'items.0.price': 1 }
)
SQL:
SELECT status, COUNT(*) as count, SUM(total) as total_amount FROM orders GROUP BY status
MongoDB:
db.collection('orders').aggregate([
{
$group: {
_id: "$status",
status: { $first: "$status" },
count: { $sum: 1 },
total_amount: { $sum: "$total" }
}
}
])
SQL:
SELECT u.name, o._id as order_id, o.total FROM users u JOIN orders o ON u._id = o.userId
MongoDB:
db.collection('users').aggregate([
{
$lookup: {
from: "orders",
localField: "_id",
foreignField: "userId",
as: "orders"
}
},
{ $unwind: { path: "$orders", preserveNullAndEmptyArrays: true } },
{
$project: {
name: 1,
order_id: "$orders._id",
total: "$orders.total"
}
}
])
npm install @queryleaf/lib
QueryLeaf takes your existing MongoDB client. It never creates or manages MongoDB connections on its own.
import { QueryLeaf } from '@queryleaf/lib';
import { MongoClient } from 'mongodb';
// Your existing MongoDB client
const mongoClient = new MongoClient('mongodb://localhost:27017');
await mongoClient.connect();
// Create QueryLeaf with your MongoDB client
const queryLeaf = new QueryLeaf(mongoClient, 'mydatabase');
// Execute SQL queries against your MongoDB database
const results = await queryLeaf.execute('SELECT * FROM users WHERE age > 21');
console.log(results);
// When you're done, close your MongoDB client
// (QueryLeaf never manages MongoDB connections)
await mongoClient.close();
For testing or debugging without a real database, use DummyQueryLeaf:
import { DummyQueryLeaf } from '@queryleaf/lib';
// Create a DummyQueryLeaf instance for testing
const queryLeaf = new DummyQueryLeaf('mydatabase');
// Operations will be logged to console but not executed
await queryLeaf.execute('SELECT * FROM users WHERE age > 21');
// [DUMMY MongoDB] FIND in mydatabase.users with filter: { "age": { "$gt": 21 } }
// [DUMMY MongoDB] Executing find on users
The repository includes several examples:
src/examples/existing-client-demo.ts
- Shows how to use QueryLeaf in a real applicationsrc/examples/basic-usage.ts
- Demonstrates basic usage with an existing MongoDB clientsrc/examples/dummy-client-demo.ts
- Shows how to use DummyQueryLeaf for testing
You can run the examples with:
# Main application example
ts-node src/examples/existing-client-demo.ts
# Basic usage example
npm run example
# Dummy client example
ts-node src/examples/dummy-client-demo.ts
Here are some practical SQL queries you can use with QueryLeaf:
-- Query users by nested address field
SELECT name, email, address.city FROM users WHERE address.zip = '10001'
-- Insert with nested document structure
INSERT INTO users (name, age, email, address) VALUES
('Jane Smith', 28, '[email protected]', {
"street": "456 Park Ave",
"city": "Chicago",
"state": "IL",
"zip": "60601"
})
-- Update a nested field
UPDATE users SET address.city = 'San Francisco', address.state = 'CA' WHERE _id = '123'
-- Query by array element property
SELECT userId, total FROM orders WHERE items[0].name = 'Laptop'
-- Filter by array element condition
SELECT * FROM orders WHERE items[1].price < 50
-- Insert document with array field
INSERT INTO orders (userId, items, status) VALUES
('user123', [
{ "id": "prod1", "name": "Monitor", "price": 300 },
{ "id": "prod2", "name": "Keyboard", "price": 75 }
], 'pending')
-- Using GROUP BY with aggregation functions
SELECT category, COUNT(*) as count, AVG(price) as avg_price FROM products GROUP BY category
-- JOIN between users and orders
SELECT u.name, o.total, o.status FROM users u JOIN orders o ON u._id = o.userId WHERE o.total > 100
Check out the examples folder for more complete examples, including how to set up the QueryLeaf instance and execute these queries.
This library demonstrates:
- Basic SELECT queries
- Filtering with WHERE clauses
- Sorting with ORDER BY
- INSERT, UPDATE, and DELETE operations
- Accessing nested fields with dot notation
- Accessing array elements with indexing
- Aggregation with GROUP BY and aggregation functions
- Joining collections with JOIN syntax
QueryLeaf follows a modular architecture:
- SqlParser: Converts SQL text into an abstract syntax tree (AST) using node-sql-parser
- SqlCompiler: Transforms the AST into MongoDB commands
- CommandExecutor: Executes the commands against a MongoDB database
The project includes both unit tests and integration tests:
Run unit tests with:
npm run test:unit
Unit tests are located in the tests/unit
directory and focus on testing the parsing and compilation of SQL statements without requiring a database connection.
Integration tests use testcontainers to spin up a MongoDB instance in Docker. Make sure you have Docker installed and running before executing these tests.
Run integration tests with:
npm run test:integration
Integration tests are located in the tests/integration
directory and test the complete functionality with a real MongoDB database.
These tests will:
- Start a MongoDB container
- Load fixture data
- Run a series of SQL queries against the database
- Verify the results
- Clean up the container when done
To run all tests:
npm run test
This project uses GitHub Actions for continuous integration. The CI workflow automatically runs on:
- All pushes to the
main
branch - All pull requests targeting the
main
branch
The CI workflow:
- Sets up Node.js (versions 16.x, 18.x, and 20.x)
- Installs dependencies
- Runs unit tests
- Runs integration tests with MongoDB in a Docker container
- Performs type checking
- Builds the package
You can see the workflow configuration in .github/workflows/test.yml
.
Comprehensive documentation is available at queryleaf.com/docs, including:
- Detailed installation and setup guides
- In-depth explanation of supported SQL syntax
- Usage examples and best practices
- Troubleshooting and debugging guides
- Performance optimization tips
For local development, you can run the documentation site with:
# Install required packages
pip install -r requirements.txt
# Serve the documentation locally
npm run docs:serve
QueryLeaf is dual-licensed:
- AGPL-3.0 for open source use
- Commercial license for commercial use with embedding
For commercial licensing options and pricing, please visit queryleaf.com or contact us at [email protected].