• Stars
    star
    588
  • Rank 75,756 (Top 2 %)
  • Language
    JavaScript
  • License
    ISC License
  • Created about 9 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

ES6 tagged template strings for prepared SQL statements 📋

SQL Template Strings

npm downloads build codecov dependencies node license chat: on gitter code style: prettier semantic-release

API Documentation

A simple yet powerful module to allow you to use ES6 tagged template strings for prepared/escaped statements.
Works with mysql, mysql2, postgres and sequelize.

Example for escaping queries (callbacks omitted):

const SQL = require('sql-template-strings')

const book = 'harry potter'
const author = 'J. K. Rowling'

// mysql:
mysql.query('SELECT author FROM books WHERE name = ? AND author = ?', [book, author])
// is equivalent to
mysql.query(SQL`SELECT author FROM books WHERE name = ${book} AND author = ${author}`)

// postgres:
pg.query('SELECT author FROM books WHERE name = $1 AND author = $2', [book, author])
// is equivalent to
pg.query(SQL`SELECT author FROM books WHERE name = ${book} AND author = ${author}`)

// sequelize:
sequelize.query('SELECT author FROM books WHERE name = ? AND author = ?', {replacements: [book, author]})
// is equivalent to
sequelize.query(SQL`SELECT author FROM books WHERE name = ${book} AND author = ${author}`)

This might not seem like a big deal, but when you do an INSERT with a lot columns writing all the placeholders becomes a nightmare:

db.query(
  'INSERT INTO books (name, author, isbn, category, recommended_age, pages, price) VALUES (?, ?, ?, ?, ?, ?, ?)',
  [name, author, isbn, category, recommendedAge, pages, price]
)
// is better written as
db.query(SQL`
  INSERT
  INTO    books
          (name, author, isbn, category, recommended_age, pages, price)
  VALUES  (${name}, ${author}, ${isbn}, ${category}, ${recommendedAge}, ${pages}, ${price})
`)

Also template strings support line breaks, while normal strings do not.

How it works

The SQL template string tag transforms the template string and returns an object that is understood by both mysql and postgres:

const query = SQL`SELECT author FROM books WHERE name = ${book} AND author = ${author}`
typeof query // => 'object'
query.text   // => 'SELECT author FROM books WHERE name = $1 AND author = $2'
query.sql    // => 'SELECT author FROM books WHERE name = ? AND author = ?'
query.values // => ['harry potter', 'J. K. Rowling']

Building complex queries with append()

It is also possible to build queries by appending another query or a string with the append() method (returns this for chaining):

query.append(SQL`AND genre = ${genre}`).append(' ORDER BY rating')
query.text   // => 'SELECT author FROM books WHERE name = $1 AND author = $2 AND genre = $3 ORDER BY rating'
query.sql    // => 'SELECT author FROM books WHERE name = ? AND author = ? AND genre = ? ORDER BY rating'
query.values // => ['harry potter', 'J. K. Rowling', 'Fantasy'] ORDER BY rating

This allows you to build complex queries without having to care about the placeholder index or the values array:

const query = SQL`SELECT * FROM books`
if (params.name) {
  query.append(SQL` WHERE name = ${params.name}`)
}
query.append(SQL` LIMIT 10 OFFSET ${params.offset || 0}`)

Raw values

Some values cannot be replaced by placeholders in prepared statements, like table names. append() replaces the SQL.raw() syntax from version 1, just pass a string and it will get appended raw.

Please note that when inserting raw values, you are responsible for quoting and escaping these values with proper escaping functions first if they come from user input (E.g. mysql.escapeId() and pg.escapeIdentifier()). Also, executing many prepared statements with changing raw values in a loop will quickly overflow the prepared statement buffer (and destroy their performance benefit), so be careful.

const table = 'books'
const order = 'DESC'
const column = 'author'

db.query(SQL`SELECT * FROM "`.append(table).append(SQL`" WHERE author = ${author} ORDER BY ${column} `).append(order))

// escape user input manually
mysql.query(SQL`SELECT * FROM `.append(mysql.escapeId(someUserInput)).append(SQL` WHERE name = ${book} ORDER BY ${column} `).append(order))
pg.query(SQL`SELECT * FROM `.append(pg.escapeIdentifier(someUserInput)).append(SQL` WHERE name = ${book} ORDER BY ${column} `).append(order))

Binding Arrays

To bind the array dynamically as a parameter use ANY (PostgreSQL only):

const authors = ['J. K. Rowling', 'J. R. R. Tolkien']
const query = SQL`SELECT name FROM books WHERE author = ANY(${authors})`
query.text   // => 'SELECT name FROM books WHERE author = ANY($1)'
query.values // => ['J. K. Rowling', 'J. R. R. Tolkien']

Named Prepared Statements in Postgres

Postgres has the option of naming prepared statements, which allows parsing and other work to be reused (and requires the SQL associated with the name to stay the same, with only the parameters changing). You can set the name with the setName() method:

// old way
pg.query({name: 'my_query', text: 'SELECT author FROM books WHERE name = $1', values: [book]})

// with template strings
pg.query(SQL`SELECT author FROM books WHERE name = ${book}`.setName('my_query'))

You can also set the name property on the statement object directly or use Object.assign().

Bound Statements in sequelize

By default, Sequelize will escape replacements on the client. To switch to using a bound statement in Sequelize, call useBind(). The boolean parameter defaults to true. Like all methods, returns this for chaining. Please note that as long as the bound mode is active, the statement object only supports Sequelize, not the other drivers.

// old way
sequelize.query('SELECT author FROM books WHERE name = ? AND author = ?', {bind: [book, author]})

// with template strings
sequelize.query(SQL`SELECT author FROM books WHERE name = ${book}`.useBind(true))
sequelize.query(SQL`SELECT author FROM books WHERE name = ${book}`.useBind()) // the same

// works with append (you can call useBind at any time)
const query = SQL`SELECT * FROM books`.useBind(true)
if (params.name) {
  query.append(SQL` WHERE name = ${params.name}`)
}
query.append(SQL` LIMIT 10 OFFSET ${params.offset || 0}`)

Editor Integration

Contributing

  • Tests are written using mocha
  • You can use npm test to run the tests and check coding style
  • Since this module is only compatible with ES6 versions of node anyway, use all the ES6 goodies
  • Pull requests are welcome :)

More Repositories

1

php-language-server

PHP Implementation of the VS Code Language Server Protocol 🆚↔🖥
PHP
1,126
star
2

svg-screenshots

📸🧩 Browser extension to take scalable, semantic, accessible screenshots of websites in SVG format.
TypeScript
834
star
3

vscode-php-intellisense

Advanced PHP IntelliSense for Visual Studio Code 🆚💬
TypeScript
397
star
4

dom-to-svg

Library to convert a given HTML DOM node into an accessible SVG "screenshot".
TypeScript
378
star
5

iterare

Array methods + ES6 Iterators = ❤️
TypeScript
285
star
6

cli-highlight

Syntax highlighting for your terminal 💻✨
TypeScript
282
star
7

php-advanced-json-rpc

A more advanced PHP implementation of the JSONRPC Protocol 📞❗
PHP
245
star
8

php-language-server-protocol

Protocol classes for the Language Server Protocol in PHP
PHP
216
star
9

semantic-release-docker

🐳 Set of semantic-release plugins to publish to DockerHub
JavaScript
76
star
10

sequelize-decorators

Sequelize + Decorators = ❤
TypeScript
76
star
11

PSKubectl

kubectl with the power of the object pipeline
C#
61
star
12

merkel

Handles your database migration crisis 🛄
TypeScript
42
star
13

link-preview-sidebar

Browser extension to open links in a sidebar instead of a new tab.
TypeScript
38
star
14

PowerGit

Git with the power of the PowerShell object pipeline. For macOS, Linux and Windows.
PowerShell
30
star
15

vscode-php-pack

Visual Studio Code extension pack for PHP 📦
17
star
16

vscode-css-stacking-contexts

VS Code extension to highlight stacking contexts in CSS and ineffective z-index declarations 💤
TypeScript
17
star
17

abortable-rx

Drop-in replacements for RxJS Observable methods and operators that work with AbortSignal
TypeScript
13
star
18

semantic-release-firefox

🦊📦🚀 semantic-release plugin to automatically release Firefox extensions
HTML
10
star
19

angular-async-filter

Angular2's async pipe for Angular 1. Promise in controller, value in view.
JavaScript
6
star
20

aggregate-map

Read-only ES6 Map implementation that aggregates results from multiple Maps in O(n)
TypeScript
4
star
21

PowerShellXSD

XML schema definitions for PowerShell Format and Types.ps1xml files. NOTE: MOVED TO THE OFFICIAL POWERSHELL REPO
PowerShell
4
star
22

PSDefaults

Manage macOS preferences from PowerShell
PowerShell
4
star
23

PSBuildkite

Module to interact with the Buildkite API from PowerShell
PowerShell
3
star
24

rx-component

Functional, Reactive React Components
TypeScript
3
star
25

PSTravis

Interact with the Travis API from PowerShell
PowerShell
2
star
26

iterare-php

Functional utilities for Iterators
PHP
2
star
27

npm-changing-lockfile-repro

Minimal reproduction case for https://npm.community/t/package-lock-json-keeps-changing-between-platforms-and-runs/1129/4
2
star
28

vscode-postgresql-syntax

Better syntax highlighting for PostgreSQL in VSCode
2
star
29

lint-blame

Blames your lint complaints to enable incremental adoption of new lint rules
TypeScript
2
star
30

whatwg-url-custom-host-repro

Website to show inconsistent behavior of the WHATWG URL API in different browsers when parsing custom protocols
HTML
2
star
31

olfaction

Storage server for code smell analysis data
TypeScript
1
star
32

node-date-only

DONT USE THIS ATM, WILL SEE v2 SOON
JavaScript
1
star
33

rxx

Reactive JSX
TypeScript
1
star
34

stringscore

String scoring algorithm used by VS Code ported to Go
Go
1
star
35

lspindex

CLI tool to build a symbol graph by querying a language server
TypeScript
1
star
36

php-semantic-release-test

PHP
1
star
37

sourcegraph-css-stacking-contexts

Sourcegraph extension to highlight CSS declarations that introduce new stacking contexts.
TypeScript
1
star
38

react-resizable-css-grid

TypeScript
1
star
39

PSCodeCovIo

Helper to use in CI to convert test coverage data emitted by https://github.com/pester/Pester to JSON that can be uploaded to https://codecov.io
PowerShell
1
star