
Security News
MCP Community Begins Work on Official MCP Metaregistry
The MCP community is launching an official registry to standardize AI tool discovery and let agents dynamically find and install MCP servers.
nano-queries
Advanced tools
Simple and powerful database-agnostic query builder.
The purpose of query builder is to simplify building a complex (SQL) query for database in safe manner.
The project goals and key features is:
A nano-queries
is focused to be an ideal query builder, that's all. There's no a queries executor, data mapper (Active Record or something), ORM, etc.
Project design is follows the UNIX philosophy, that's why this solution works well with any database - SQLite, Postgres, MySQL, Oracle, etc.
A nano-queries
design is based on the idea that only the programmer knows how to implement the ideal query properly, and the query builder must not limit the programmer in doing their work.
That's why the core concept of nano-queries
is query segments.
A query segments is a nodes that represents a raw code and data (user input), and may be compiled to a query and bindings.
All query segments is based on 3 primitives:
RawSegment
represents a raw code in query and will be compiled as isPreparedValue
represents an user input and will be replaced with a placeholder while compilingQuery
represents a collection of query segments and may contains another instances of Query
. Compiler will handle all segments inside Query
recursivelyAdditionally, there are QueryBuilder
that extends Query
and let you build queries step by step like that
import { QueryBuilder } from 'nano-queries/QueryBuilder';
import { SQLCompiler } from 'nano-queries/compilers/SQLCompiler';
const compiler = new SQLCompiler();
compiler.toSQL(
new QueryBuilder({ join: ' ' })
.raw('SELECT * FROM notes WHERE id IN')
.raw(
new QueryBuilder()
.raw('(SELECT note_id FROM tags WHERE name=')
.value('personal')
.raw(')')
)
.raw('LIMIT').value(100)
.raw('OFFSET').value(200)
);
And TemplateStringQueryBuilder
that let you build queries like templated string
import { TemplateStringQueryBuilder } from 'nano-queries/TemplateStringQueryBuilder';
import { SQLCompiler } from 'nano-queries/compilers/SQLCompiler';
const compiler = new SQLCompiler();
const sql = new TemplateStringQueryBuilder();
const userInput = {
name: "foo",
limit: 100,
offset: 200,
};
compiler.toSQL(
sql.build`
SELECT * FROM notes
WHERE id IN (
SELECT note_id FROM tags WHERE name=${userInput.name}
)
LIMIT ${userInput.limit} OFFSET ${userInput.offset}`
);
Both examples above yields query object equivalent to
{
"sql": "SELECT * FROM notes WHERE id IN (SELECT note_id FROM tags WHERE name=?) LIMIT ? OFFSET ?",
"bindings": ["foo", 100, 200],
}
User input for both cases will be replaced to placeholder and present in bindings list.
With nano-queries
you may build queries for any database, including SQLite, Postgres, MariaDB, and even embedded databases implemented on JavaScript or WASM like PGlite.
Moreover, you may build not only SQL queries, but any text query and bindings for it. For example, you may build a complex and safe queries for GraphQL, Redis Lua Scripting, SPARQL, gRPC, etc.
That's important feature and reason why nano-queries
stands out among other solutions.
You may just configure queries compiler, to build queries for your target database dialect, or even implement your own compiler.
import { SQLCompiler } from 'nano-queries/compilers/SQLCompiler';
import { QueryBuilder } from 'nano-queries/QueryBuilder';
export const compiler = new SQLCompiler({
// Optionally, you may configure queries compiler, for your database.
// In this example we configure placeholders to generate SQL for Postgres
// By default will be used anonymous placeholders (symbol `?`) that used in SQLite
getPlaceholder(index) {
return '$' + (index + 1);
},
});
compiler.toSQL(
new QueryBuilder({ join: ' ' })
.raw('SELECT * FROM notes WHERE id IN')
.raw(
new QueryBuilder()
.raw('(SELECT note_id FROM tags WHERE name=')
.value('personal')
.raw(')')
)
.raw('LIMIT').value(100)
.raw('OFFSET').value(200)
);
Code above yields query object equal to
{
"sql": "SELECT * FROM notes WHERE id IN (SELECT note_id FROM tags WHERE name=$1) LIMIT $2 OFFSET $3",
"bindings": ["foo", 100, 200],
}
Even though you may build queries step by step with QueryBuilder
as in examples above, you may also use custom queries from nano-queries/sql
or implement your own custom queries.
Custom query segment is a class that extends a basic class nano-queries/core/Query
and let you construct complex pieces of queries like SQL expressions, clauses, operators, etc.
Example with implementation of nano-queries/sql/LimitClause
:
import { Query } from 'nano-queries/core/Query';
import { QueryBuilder } from 'nano-queries/QueryBuilder';
import { IQuery, QuerySegment } from 'nano-queries/types';
export class LimitClause extends Query implements IQuery {
private readonly state;
constructor(state: { limit?: number; offset?: number }) {
super();
this.state = state;
}
public getSegments(): QuerySegment[] {
const { limit, offset } = this.state;
const query = new QueryBuilder({ join: ' ' });
if (limit) {
query.raw('LIMIT').value(limit);
}
if (offset) {
query.raw('OFFSET').value(offset);
}
return query.getSegments();
}
}
Now you may use new query segment LimitClause
in your queries like that:
import { QueryBuilder } from 'nano-queries/QueryBuilder';
import { SQLCompiler } from 'nano-queries/compilers/SQLCompiler';
const compiler = new SQLCompiler()
test('Limit and offset appends as placeholders', () => {
const query = new QueryBuilder({ join: ' ' }).raw(
'SELECT * FROM foo',
new LimitClause({ limit: 100, offset: 200 })
);
expect(compiler.toSQL(query)).toEqual({
sql: 'SELECT * FROM foo LIMIT ? OFFSET ?',
bindings: [100, 200],
});
});
nano-queries
provides some basic query segments for SQL by path nano-queries/sql
.
The purpose of basic query segments is to simplify composing a routine queries.
Implementation of some basic query segments may be not ideal for now or something may be missed. In that case you should implement them itself, but you are welcome to make issues with requests for new query segments you missing.
nano-queries
provides a configurable query builder, that have methods to make query segments.
That's may be useful to pack all query segments used in your project at single place.
Usage looks similar to Zod validator - you compose a query like some structure from blocks.
import { SQLCompiler } from 'nano-queries/compilers/SQLCompiler';
import { ConfigurableSQLBuilder } from 'nano-queries/sql/ConfigurableSQLBuilder';
// You may configure query builder for you database once as singleton,
// then export it and use everywhere in your application
export const qb = new ConfigurableSQLBuilder(new SQLCompiler({
// Optionally, you may configure queries compiler, for your database.
// In this example we configure placeholders to generate SQL for Postgres
// By default will be used anonymous placeholders (symbol `?`) that used in SQLite
getPlaceholder(index) {
return '$' + (index + 1);
},
}));
// Now you may build query
qb.toSQL(
qb.line(
// You may insert strings with no wrap it to `qb.raw`, they will be converted to raw segments
qb.raw('SELECT * FROM notes'),
qb.where(
qb.condition(
qb.raw('workspace_id=')
// That's important to insert user input only with `value` method,
// to insert placeholder while compiling query
.value('2ecdc7e5-734e-47a9-b053-f399eb225d7b')
)
.and(
qb.line('id IN').raw(
qb.group(
qb
.line('SELECT target FROM attachedTags')
.raw(
qb.where(
qb
.line('source IN')
.raw(
qb
.values(['foo', 'bar', 123])
.withParenthesis(),
),
),
),
),
)
),
),
qb.limit(20),
qb.offset(10),
)
);
Code above yields query object equal to
{
"sql": "SELECT * FROM notes WHERE workspace_id=$1 AND id IN (SELECT target FROM attachedTags WHERE source IN ($2,$3,$4)) LIMIT $5 OFFSET $6",
"bindings": ["2ecdc7e5-734e-47a9-b053-f399eb225d7b", "foo", "bar", 123, 20, 10],
}
Just like use query builder object you may manually build queries with query segments that is classes which may be composed and nested.
import { QueryBuilder } from 'nano-queries/QueryBuilder';
import { PreparedValue } from 'nano-queries/core/PreparedValue';
import { SQLCompiler } from 'nano-queries/compilers/SQLCompiler';
import { ConditionClause } from 'nano-queries/sql/ConditionClause';
import { GroupExpression } from 'nano-queries/sql/GroupExpression';
import { WhereClause } from 'nano-queries/sql/WhereClause';
import { LimitClause } from 'nano-queries/sql/LimitClause';
const compiler = new SQLCompiler()
const where = new WhereClause();
const query = new QueryBuilder({ join: ' ' })
// You may insert many segments into `raw` method
.raw('SELECT * FROM foo', where)
// or call `raw` to add every segment
.raw(new LimitClause({ limit: 100, offset: 200 }));
// You can fill a `where` after compose a query object.
// All segments have their own state and query will be
// build only during compiling, so code pieces,
// values and placeholders will be on the correct place
where
.and('x > ', new PreparedValue(0))
.or(
// Add brackets for nested condition
new GroupExpression(
new ConditionClause()
.and('y=', new PreparedValue(1))
.and('z=', new PreparedValue(2)),
),
);
compiler.toSQL(query);
Code above yields query object equal to
{
"sql": "SELECT * FROM foo WHERE x > ? OR (y=? AND z=?) LIMIT ? OFFSET ?",
"bindings": [0, 1, 2, 100, 200],
}
TODO: add docs
nano-queries
is an truth open source project, so you are welcome on project github repository to contribute a code, make issues with feature requests and bug reports.
You may contribute to a project if you tell about nano-queries
to your friends. Let's hit by garbage ORMs and monstrous "query builders" together.
FAQs
Simple and powerful database-agnostic query builder (SQL & NoSQL)
The npm package nano-queries receives a total of 29 weekly downloads. As such, nano-queries popularity was classified as not popular.
We found that nano-queries demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer 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
The MCP community is launching an official registry to standardize AI tool discovery and let agents dynamically find and install MCP servers.
Research
Security News
Socket uncovers an npm Trojan stealing crypto wallets and BullX credentials via obfuscated code and Telegram exfiltration.
Research
Security News
Malicious npm packages posing as developer tools target macOS Cursor IDE users, stealing credentials and modifying files to gain persistent backdoor access.