mrkeyoor.com_
Wed 23 Sept 02:50 UTC
npmDataupdated 22 Sept 2026

sql-escaper review

sql-escaper 1.5.1 is a MySQL-specific SQL string formatter descended from `sqlstring`. It renders JavaScript values as literals, quotes identifiers, substitutes `?` and `??`, expands lists and bulk rows, and permits explicit raw fragments. Its parser limits object-to-assignment expansion to `SET` and `ON DUPLICATE KEY UPDATE`, closing a query-shape injection path in older formatter/driver combinations. The current patch makes its `TemporalValue` TypeScript type self-contained; it still creates SQL text rather than server-bound parameters.

Verdict

sql-escaper 1.5.1 installed in 0.3 seconds with 0 dependencies and 0 audit findings in our sandbox, while its browser-target build failed. Use it for the documented mysqljs/mysql override or unavoidable MySQL string formatting; do not add it beside current MySQL2 or confuse `?` replacement with prepared statements.

We installed it

Lab card: what happened when we installed sql-escaperScreenshot of sql-escaper documentation
Install✓ · 0.3s1 package on disk · 1 MB
ImportESM import works · require() works · CommonJS package with exports map
Browsern/acould not be bundled for the browser (Node-only code, most likely)
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does sql-escaper install cleanly?

Yes. In a fresh container with an empty cache, npm install sql-escaper finished in 0.3s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

Can sql-escaper run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does sql-escaper work with both ESM and CommonJS?

Yes. Both import 'sql-escaper' and require('sql-escaper') worked in Node 22 in our run. The package is published as CommonJS with an exports map.

Does sql-escaper include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

sql-escaper or mysql2: which should you use?

mysql2: Use the complete MySQL driver for bound parameters and prepared statements; current versions already include this escaper. sql-escaper 1.5.1 installed in 0.3 seconds with 0 dependencies and 0 audit findings in our sandbox, while its browser-target build failed.

When should you not use sql-escaper?

You already run MySQL2 3.17.0 or newer. Its README says sql-escaper is the default escaping library, so another direct install adds no protection.

API stability4/5The package intentionally retains `sqlstring`'s 4 familiar calls: `escape`, `escapeId`, `format`, and `raw`. Version 1.5.1 changes a self-contained TypeScript type rather than runtime output. Context-aware clause parsing is more behavior-sensitive than plain substitution, however, and this standalone 1.x project began in 2026. Query snapshots should cover every object, list, date, and raw-fragment shape used by an application.
Docs5/5The README documents Node, Bun, and Deno installation; CommonJS and ESM imports; every public function; primitive, Date, Temporal, binary, object, array, Set, Map, identifier, and bulk-row output; and the two driver migration paths. It also states the dangerous parts directly: formatting is not preparation, all question marks are replaced, raw values bypass escaping, and `NO_BACKSLASH_ESCAPES` is incompatible.
Maintenance4/5Version 1.5.1 and the latest repository push both date to July 12, 2026. The unarchived repository has 0 open issues and pull requests in GitHub's combined counter, plus CI for Node, Bun, and Deno. The current patch fixes the exported `TemporalValue` type. Long-term history is short, and the repository has 1 star, so continued driver adoption matters more than its standalone popularity.
Ecosystem4/5The npm endpoint counted 4,820,287 downloads in the latest completed week. MySQL2 adopted sql-escaper as its default formatter from version 3.17.0, and the README supplies a compatible npm override for mysqljs/mysql. The `sqlstring`-shaped API, bundled types, and 3 supported server runtimes ease migration. Its deliberate MySQL grammar makes it unsuitable as a general SQL abstraction.

Use it if

  • A mysqljs/mysql 2.18.1 application needs the README's npm override from `sqlstring` to the safer context-aware formatter.
  • MySQL SQL must be rendered outside a driver's parameter API, with compatible `escape`, `escapeId`, `format`, and `raw` calls.
  • BigInt, Uint8Array, Map, Set, or Temporal values need the package's documented MySQL literal handling.
  • Object patches should expand in recognized assignment clauses while ordinary value placeholders stringify objects instead of reshaping the query.
Skip it if

Setup reality

We installed sql-escaper 1.5.1 in 0.3 seconds in a fresh Node 22 container. npm left 1 package and 1 MB on disk. The package is 88 KB unpacked with 0 dependencies and 0 peers, includes TypeScript declarations, and produced 0 audit findings. require() and ESM import both worked through its CommonJS package and exports map. Our esbuild browser build failed instead of producing an artifact.

Installation has no native build, credentials, or config file. The operational decision is whether SQL string construction is justified. MySQL2 3.17.0+ already uses this formatter internally. For mysqljs/mysql 2.18.1, the README gives an npm overrides entry that replaces sqlstring; perform a clean install, inspect the lockfile, and regression-test generated statements because the override changes a transitive query boundary. Version 1.5.1 requires Node 12+, Bun 1+, or Deno 2+.

format() looks like placeholder binding, yet it replaces values locally before the query reaches MySQL. It also treats question marks in comments and quoted text as candidates, so templates containing literal ? characters need care. The escape rules assume NO_BACKSLASH_ESCAPES is off. Objects expand only in parsed assignment positions, but keys must still be allowlisted. Arrays become comma lists, nested arrays become bulk rows, and an empty array can yield invalid IN () SQL.

raw() and a custom toSqlString() bypass escaping and should accept only fixed program text. NaN and Infinity are emitted as text that MySQL rejects, so validate finite numbers first. Date formatting uses local time unless a timezone is supplied, while invalid dates become NULL. Pass Z when UTC is intended. The failed browser build is consistent with a server-side database utility; keep it out of frontend bundles and prefer the driver's actual parameter or prepared-statement path whenever available.

Patterns

Render one scalar as a MySQL literal escape-scalar-value

import { escape } from 'sql-escaper';

const literal = escape("O'Reilly", true);
// => "'O\\'Reilly'"

The true argument stringifies plain objects instead of turning them into assignments. Driver-bound parameters remain preferable for query values.

Quote a qualified MySQL identifier escape-identifier

import { escapeId } from 'sql-escaper';

const column = escapeId('users.display_name');
// => '`users`.`display_name`'

Validate identifiers against an allowlist before quoting. Data values belong in `escape()` or a value placeholder, without backticks.

Substitute an identifier and value format-values-and-identifiers

import { format } from 'sql-escaper';

const sql = format('SELECT * FROM ?? WHERE id = ?', ['users', 42]);
// => 'SELECT * FROM `users` WHERE id = 42'

A double question mark quotes the identifier, while a single question mark renders the value. The result is a complete SQL string, not a statement with server-bound parameters.

Expand an allowlisted SET patch format-set-clause

const patch = { name: 'Ada', email: 'ada@example.com' };
const sql = format('UPDATE users SET ? WHERE id = ?', [patch, 42]);

The parser expands objects in recognized SET clauses. Filter request keys before formatting so callers cannot choose arbitrary columns.

Build a duplicate-key update format-upsert

const sql = format(
  'INSERT INTO users (email, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE ?',
  ['ada@example.com', 'Ada', { name: 'Ada' }],
);

The third value expands because the parser recognizes the duplicate-key assignment clause. Its object keys still require validation.

Create a value list for IN format-in-list

const ids = [3, 5, 8];
const sql = format('SELECT * FROM users WHERE id IN (?)', [ids]);
// => 'SELECT * FROM users WHERE id IN (3, 5, 8)'

An empty array can produce invalid `IN ()` syntax. Handle 0 values with an explicit always-false query branch.

Render rows for a bulk insert format-bulk-values

const rows = [[1, 'Ada'], [2, 'Grace']];
const sql = format('INSERT INTO users (id, name) VALUES ?', [rows]);

Nested arrays become grouped rows. Verify every row has 2 fields here and cap batch size before building a large SQL string.

Format a Date explicitly in UTC escape-date-utc

const createdAt = new Date('2026-08-08T12:00:00.000Z');
const sqlDate = escape(createdAt, true, 'Z');
// => "'2026-08-08 12:00:00.000'"

Passing `Z` prevents host-timezone drift. An invalid Date becomes `NULL` instead of producing an exception.

Convert bytes to a MySQL hex literal escape-binary-data

const bytes = new Uint8Array([0, 1, 254, 255]);
const literal = escape(bytes, true);
// => "X'0001feff'"

Buffer and Uint8Array values produce hex literals. Enforce byte limits before turning a large payload into query text.

Insert a fixed SQL function use-trusted-raw-fragment

import { raw } from 'sql-escaper';

const sql = format('UPDATE jobs SET processed_at = ? WHERE id = ?', [raw('NOW()'), 42]);

`raw()` performs 0 escaping. Its argument must be fixed application code, never request data or a user-selected expression.

Replace mysqljs/mysql's formatter override-sqlstring

{
  "dependencies": { "mysql": "^2.18.1" },
  "overrides": { "sqlstring": "npm:sql-escaper" }
}

This is the README's npm override for mysqljs/mysql 2.18.1. Clean-install, inspect the lockfile, and compare generated query snapshots.

Block unsupported numeric literals reject-nonfinite-number

function finiteSqlNumber(value: number): string {
  if (!Number.isFinite(value)) throw new TypeError('Expected a finite number');
  return escape(value, true);
}

sql-escaper leaves `NaN` and `Infinity` unchanged. MySQL rejects both texts, so validate before formatting.

Alternatives

PackageRegistryPick it when
mysql2npmUse the complete MySQL driver for bound parameters and prepared statements; current versions already include this escaper.
sqlstringnpmKeep it only where exact legacy output is mandatory and all object inputs are tightly controlled.
pg-formatnpmUse it for PostgreSQL identifier and literal formatting instead of applying MySQL backtick rules.
sql-template-tagnpmUse it when a tagged template should retain values separately for a compatible database driver.

More data guides

numpy · fsspec · pandas · sqlalchemy · pyarrow · lxml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.