squel builds SQL strings and leaves the connection to somebody else
:office: SQL query string builder for Javascript
At a glance
- What is it?
- squel is a TypeScript SQL query string builder with a fluent API, zero runtime dependencies and dual ESM, CommonJS and browser builds. It never opens a connection and never runs a query, which makes it a good fit for a service that owns its own database layer, and a bad fit for a client that needs one.
- Who is it for?
- Adopt squel when you already have a database driver and want query construction that produces a string you can inspect, log and test, and when you need dialect features such as CTEs, UPSERT, MERGE or RETURNING that a simple builder omits. Do not adopt it if you want an ORM, since squel does not connect, execute, migrate or map rows, and the readme points at Knex for that.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 75 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A builder that stops at the string
squel is a SQL query string builder for JavaScript and TypeScript, and the most useful thing about it is where it stops.
It builds a string. It does not open a connection, it does not run the query, it does not map rows, and it does not migrate anything. The output of the whole library is text, and the text is handed to whatever driver you already use.
That boundary is the design. The dependency list in the package manifest is empty, there is no runtime dependency at all, and the library works in Node.js and in the browser. A query builder that ships no driver can be used on a server that talks to Postgres through a pool, in a browser that has to emit a query for a reporting API to run, or in a test that asserts on the exact SQL a function produces.
It is written in TypeScript with first class type definitions, the licence is MIT, and the package metadata shows version 6.5.0 with the author Ramesh Nair. The last push to the repository was on 2026-08-09, and the repository has no GitHub releases, so the version history lives in the CHANGELOG.md and in the npm registry.
The readme is direct about the fit. Squel is suitable for production use, and if you want richer ORM features you may also want to consider Knex.
Four verbs and a fluent chain
The API is a method chain, and the shape of it is the same for every query type.
The standard verbs are supported, SELECT, UPDATE, INSERT and DELETE, and each starts from a top level call on the squel object.
import squel from "squel"
const query = squel.select().from("books").field("title").toString()A SELECT chains from, field, where, group, order and limit, and the more advanced forms are where the library earns its place. Joins take a table, an optional alias and a condition, a field can be given an alias, and a where clause can take a raw fragment with a question mark and a list of values.
The alias handling is worth noting because it is the sort of detail that separates a builder from a string concatenation helper. A field can be given a name, and with autoQuoteFieldNames enabled the identifiers are quoted for you, which is the difference between a query that works on a table called order and one that does not.
UPDATE and INSERT both use set, and UPDATE can set several tables at once, including expressions such as a count incremented by one, which is passed as a key with an undefined value to mark it as a raw assignment. DELETE takes the same where, order and limit chain.
Everything ends in toString, or in toParam, which is the other way out.
toParam is the one that matters for user input
Squel supports parameterized queries for safe value escaping, and the mechanism is a second output method rather than a flag.
Calling toString produces the SQL with the values already substituted into it. Calling toParam produces an object with a text field and a values field, where the text holds question mark placeholders and the values hold the parameters in order.
squel.select({ autoQuoteFieldNames: true })
.from("table", "t1")
.field("t1.id")
.where("age IN ?", squel.str('RANGE(?, ?)', 1, 1.2))
.toParam()The nested squel.str call is the part that trips people up. A plain question mark in a where clause takes a single value, and to build an IN list you need the raw fragment helper, where the string contains its own placeholders and the values are passed alongside it.
The distinction between the two output methods is the security boundary of the library, and it is worth being blunt about it. A query built with toString and then handed to a driver that interpolates the values is not parameterised, whatever the builder did, so the rule is simple. If any part of the query comes from a user, use toParam and let the driver bind the values.
There is also a documented warning about a different mistake, and it is the one that appears in the readme as a Warning block. Do not ever pass queries generated on the client side to your web server for execution, because that configuration would make it trivial for a casual attacker to execute arbitrary queries, as with an SQL injection vector but much easier to exploit and practically impossible to protect against.
Expressions and subqueries compose with the same API
Two features make squel usable for queries that a simple builder cannot express.
The first is the expression builder. squel.expr() produces a fragment that can be combined with and and or, and fragments nest inside each other, so a condition with parentheses inside it is built the same way as a top level where clause.
squel.expr()
.and("test = 3")
.and(
squel.expr()
.or("inner = 1")
.or("inner = 2")
)
.toString()The result is a parenthesised group, and a where clause can take one of these fragments directly, which is how you combine a condition on a joined table with a condition on a subquery in the same where clause.
The second is nested queries, and the mechanism is that a query object is accepted wherever a table name is.
squel.select()
.from(squel.select().from("students"), "s")
.field("id")
.join(squel.select().from("marks").field("id"), "m", "m.id = s.id")
.toString()That is a derived table in the FROM clause and a derived table in a JOIN, both written as normal squel chains. Because the subquery is an object rather than a string, the aliasing and the quoting rules apply to it as well, which is the behaviour you would otherwise have to handle by hand.
Dialect features beyond the standard four verbs
The feature list is where squel is more ambitious than a typical query builder, and the modern SQL it supports is the reason to choose it over something simpler.
Common Table Expressions and recursive CTEs are supported, so a WITH clause is not something you have to hand write. Postgres and MySQL UPSERT is supported, and so is MS SQL Server MERGE, which are the two dialect specific ways of expressing an insert that updates on conflict.
Window functions are supported through a fluent OVER clause builder, so the frame specification is built with the same method chain rather than as a string fragment. Dialect aware JSON value extraction through jsonExtract is supported, which matters because the JSON functions differ between Postgres, MySQL and SQL Server and a generic builder cannot guess.
Return clauses are supported too, with Postgres and SQLite RETURNING and SQL Server OUTPUT, so you can get the affected rows back from a write. The readme also mentions support for non standard commands for MySQL, PostgreSQL and Microsoft SQL Server, which is the escape hatch for vendor specific statements.
The library is extensible as well, and the feature list describes it as letting you build any custom query or command you need, and there is a registerValue API for telling the builder how to handle your own object types, since by default object instances are not supported as field values and a Date has to be taught to it.
A package that ships four build outputs and no dependencies
The package layout section lists what actually ends up in the tarball, and the dual build is the notable part.
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
},
"./browser": "./dist/browser/squel.min.js"
}There is an ES module build, a CommonJS build, TypeScript declaration files, and a minified IIFE bundle for a script tag, which exposes squel on the window. The CommonJS usage has a small wrinkle worth knowing, because the default export is what you require, so it is require("squel").default, or the named squel export as an alternative.
The build itself is run with Bun, the build script is bun run scripts/build.ts, and the tests are run with bun test, including a coverage mode that produces lcov. Linting and formatting are done with Biome rather than with the usual eslint and prettier pair, and there is a husky directory for the git hooks, a commitlint configuration and a.versionrc.json for the version bump, which is a coherent modern JavaScript toolchain rather than a legacy one.
Node.js 18 or newer is required, and the package is published with provenance enabled. The files field limits the published tarball to dist, the README, the licence and the changelog, so the source and the tests do not ship.
The full guide and API documentation is at hiddentao.github.io/squel.
Editorial conclusion
Adopt squel when you already have a database driver and want query construction that produces a string you can inspect, log and test, and when you need dialect features such as CTEs, UPSERT, MERGE or RETURNING that a simple builder omits. Do not adopt it if you want an ORM, since squel does not connect, execute, migrate or map rows, and the readme points at Knex for that. Verify first that you call toParam rather than toString for any query carrying user input, and never execute a query that was built on the client side and sent to your server, which the project warns about explicitly.
Frequently asked questions
What is squel?
It is a flexible SQL query string builder for JavaScript and TypeScript, written in TypeScript with first class type definitions, with no runtime dependencies. It builds the SQL string and leaves connecting, executing and mapping rows to your own database layer.
How do I install squel and use it?
Run npm install squel, or use bun add, yarn add or pnpm add. Node.js 18 or newer is required. In ESM you import squel from "squel", and in CommonJS you require the default export, and the API is the same in both.
How do I build a parameterised query with squel?
Call toParam instead of toString. It returns an object with a text field holding the SQL with placeholders and a values field holding the parameters, and you pass that to your driver so the values are bound rather than interpolated.
Is it safe to run squel queries in the browser?
You can build queries in the browser, but the readme carries a warning against passing queries generated on the client side to your web server for execution, since that would make it trivial for a casual attacker to run arbitrary queries.
Does squel support modern SQL features?
Yes. It supports Common Table Expressions and recursive CTEs, Postgres and MySQL UPSERT, MS SQL Server MERGE, window functions through a fluent OVER clause builder, dialect aware jsonExtract, and RETURNING for Postgres and SQLite or OUTPUT for SQL Server.
What should I use instead of squel if I want an ORM?
The readme points at Knex for richer ORM features. Squel has no connection handling, no migrations and no row mapping, so it is a query builder rather than a data access layer.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hiddentao-squel)Was this useful?