sql-formatter
sql-formatter is a JavaScript pretty-printer for SQL strings and files. Its library API and bundled CLI parse SQL using a selected dialect, then normalize indentation, line breaks, keyword casing, operator spacing, and spacing between statements. It understands more than twenty named dialect modes, including PostgreSQL, MySQL, SQLite, BigQuery, Snowflake, Spark, DuckDB, PL/SQL, T-SQL, and Trino. It can preserve disabled regions and replace recognized placeholders, but it does not validate a query against your database or make substituted values safe.
A mature, flexible formatter and CLI for existing multi-dialect workflows, especially when configuration control matters. For a new opinionated formatting standard, start with prettier-plugin-sql-cst; for stored procedures or semantic SQL work, use a database-native formatter or parser.
Use it if
- You need one formatter for SQL generated or stored by a JavaScript application across several named database dialects
- You want a CLI that reads stdin or files, supports in-place fixes, and discovers a shared .sql-formatter.json configuration
- You need configurable casing, indentation, expression width, boolean-operator placement, and spacing between multiple statements
- You embed SQL in an editor or developer tool and need both ESM and CommonJS entry points with declarations included
- You are choosing a formatter for a new long-lived codebase and prefer active feature development: the README says this project is in maintenance mode and names prettier-plugin-sql-cst as the author's newer architecture
- You format stored procedures or scripts that change the statement delimiter: the README explicitly lists stored procedures and delimiters other than semicolon as unsupported
- You need SQL parsing, semantic validation, lint rules, or schema awareness: this is a pretty-printer and does not know whether tables, columns, functions, or types exist in your database
- You plan to put format() in a browser bundle but only need one dialect: the language option determines dialect at runtime, so the docs say every dialect must be bundled; use formatDialect() with an explicit dialect or choose a smaller tool
- You want placeholder replacement to be safe parameter binding: the params documentation says callers must escape values themselves, so using it with untrusted input can create SQL injection rather than prevent it
Setup reality
npm install sql-formatter provides dual ESM and CommonJS entry points, adjacent declaration files, and a sql-formatter executable. There are no peer dependencies or native builds. The first important choice is the dialect: language: 'sql' is only a common subset, not automatic detection, and the FAQ says a wrong or omitted dialect is the usual cause of parse errors. For browser builds, format(sql, { language }) can pull every dialect because the selection happens at runtime; formatDialect(sql, { dialect: postgresql }) was added so bundlers can retain a specific imported dialect. The CLI reads stdin by default, accepts a file, can overwrite with --fix, and searches the current directory and parents for .sql-formatter.json. That discovery is convenient locally but can make CI output depend on an unexpected parent config, so pass --config when reproducibility matters. The parser does not support stored procedures or delimiter changes beyond semicolon, and template syntax may require custom paramTypes or formatter-disable comments. Placeholder replacement is text substitution, not database binding, and the docs put escaping entirely on you. Old Webpack and Babel setups can also choke on class-property syntax unless updated or configured with preset-env. Finally, the maintainer calls the project maintenance-only, so expect feasible bug fixes rather than new language architecture or major formatting features.
Patterns
Format a SQL string with an explicit dialectformat-sql
import { format } from 'sql-formatter';
const output = format('select id,name from users where active=true', {
language: 'postgresql',
});
console.log(output);Always choose the real dialect; the default sql mode is a common subset and does not detect syntax automatically.
Control casing and layoutconfigure-style
const output = format(sql, {
language: 'mysql',
tabWidth: 4,
keywordCase: 'upper',
dataTypeCase: 'upper',
functionCase: 'lower',
logicalOperatorNewline: 'before',
expressionWidth: 60,
});identifierCase is experimental, and indentStyle is deprecated, so avoid baking either into a new shared standard.
Import one dialect for a browser bundleimport-one-dialect
import { formatDialect, postgresql } from 'sql-formatter';
const output = formatDialect(sql, {
dialect: postgresql,
keywordCase: 'upper',
});formatDialect enables static dialect selection; format() with language can cause a bundler to include every dialect.
Format piped SQL with the CLIformat-from-stdin
echo 'select * from users where id = 3' | npx sql-formatter -l postgresqlWithout -l or a config file, the CLI uses the generic sql dialect rather than guessing PostgreSQL.
Rewrite a file in placefix-sql-file
npx sql-formatter --fix -l snowflake queries/report.sql--fix overwrites the input file; run it through version control or CI checks so unintended formatting is recoverable.
Create a project CLI configurationshare-cli-config
{
"language": "bigquery",
"tabWidth": 2,
"keywordCase": "upper",
"linesBetweenQueries": 2
}Save this as .sql-formatter.json; the CLI searches parent directories, so use --config in CI if parent files could vary.
Display positional parameter valuesreplace-positional-params
const output = format(
'SELECT * FROM users WHERE email = ? AND age >= ?',
{
language: 'mysql',
params: ["'ada@example.com'", '21'],
},
);Values are inserted as SQL text and are not escaped; never use params as a substitute for your database driver's parameter binding.
Display named T-SQL parameter valuesreplace-named-params
const output = format(
'SELECT * FROM users WHERE name = @name AND age = @age',
{
language: 'transactsql',
params: { name: "'Ada'", age: '37' },
},
);Recognized placeholder forms differ by dialect; @name works by default for Transact-SQL but not every language.
Display PostgreSQL numbered parametersreplace-numbered-params
const output = format(
'SELECT * FROM events WHERE owner_id = $1 AND created_at >= $2',
{
language: 'postgresql',
params: { 1: '42', 2: "'2026-01-01'" },
},
);Object keys map to the numeric placeholder labels; the replacement strings still need correct SQL quoting.
Teach the parser a custom template placeholderpreserve-template-placeholders
const output = format(
'SELECT {columns} FROM {table} WHERE id = {id}',
{
language: 'sql',
paramTypes: {
custom: [{ regex: String.raw`\{[a-zA-Z0-9_]+\}` }],
},
},
);Custom placeholders help parsing common templates but cannot make arbitrary template control flow valid SQL.
Leave a troublesome region untoucheddisable-formatting-region
const sql = `
/* sql-formatter-disable */
SELECT {{ dynamic_columns }} FROM {{ dynamic_table }};
/* sql-formatter-enable */
SELECT id, name FROM users;
`;
const output = format(sql, { language: 'sql' });Disabled text is not parsed at all, which is useful for templates but can also hide malformed SQL from formatter checks.
Space multiple SQL statements consistentlyseparate-statements
const output = format(
'SELECT 1; SELECT 2; SELECT 3;',
{
language: 'sql',
linesBetweenQueries: 2,
newlineBeforeSemicolon: false,
},
);Statement splitting assumes semicolons; changing the delimiter to forms such as MySQL DELIMITER is not supported.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| prettier-plugin-sql-cst | npm | Use it for new opinionated SQL formatting built on Prettier's layout algorithm and the current maintainer's newer parser architecture. |
| node-sql-parser | npm | Use it when you need a SQL AST, table and column analysis, or query rewriting rather than only pretty-printing. |
| @sqltools/formatter | npm | Use it when you are already in the SQLTools ecosystem and its smaller set of formatting conventions and dialects is sufficient. |