Transforms a template literal in an object that can be read by node-postgres.
npm version install size circleci coverage
- Written in Typescript
- Lightweight (less than 50 lines of code)
- Fully tested (100% coverage)
- Works with nested sql tags
- Works with conditions inside expressions
- Compatible with node-postgres, with a useful shorthand
npm install @sequencework/sql --save
(or with yarn, yarn add @sequencework/sql
)
const sql = require('@sequencework/sql') const yearRange = [1983, 1992] const query = sql` select * from movies where year >= ${yearRange[0]} and year <= ${yearRange[1]} ` // query looks like this: // { // text: 'select * from books where author = 1ドル and year = 2ドル', // values: [1983, 1992] // }
You can also use conditions:
const sql = require('@sequencework/sql') const findBookByAuthor = author => sql` select * from books ${ // if author is undefined, it is ignored in the query author && sql`where author = ${author}` } ` // findBookByAuthor() looks like this: // { // text: 'select * from books', // values: [] // } // findBookByAuthor('steinbeck') looks like this: // { // text: 'select * from books where author = 1ドル', // values: ['steinbeck'] // }
undefined
. If it is false
, it will be added as a value.
const filterThisYear = false // does not work as expected sql` select * from books ${filterThisYear && sql`where year = 2018`} ` // instead you should do sql` select * from books ${filterThisYear ? sql`where year = 2018` : undefined} `
It's also possible to pass raw, unescaped data to your queries. For that, use sql.raw
:
const tableName = 'books' const query = sql`select * from ${sql.raw(tableName)}`
💥 Please, be careful! Remember that the raw values won't be replaced by a placeholder and thus won't be escaped!
Example with node-postgres
We start by creating a function:
// movies.js const sql = require('@sequencework/sql') const listMoviesByYear = async (db, yearRange) => { const { rows } = await db.query(sql` select * from movies where year >= ${yearRange[0]} and year <= ${yearRange[1]} `) return rows } module.exports = { listMoviesByYear }
Then, we create a singleton for the connection pool, like recommended by brianc, node-postgres's creator.
// db.js const { Pool } = require('pg') // we create a singleton here for the connection pool const db = new Pool() module.exports = db
Finally, we connect everything:
// main.js const db = require('./db') const { listMoviesByYear } = require('./movies') const main = async () => { const movies = await listMoviesByYear(db, [1983, 1992]) console.log(movies) } main()
We can even create a transaction (useless in this example, but it's just to show that our previous function is reusable):
const main = async () => { // we get a client const client = await db.connect() try { await client.query('BEGIN') const movies = await listMoviesByYear(client, [1983, 1992]) await client.query('COMMIT') } catch (e) { await client.query('ROLLBACK') } finally { client.release() } console.log(movies) }
Since we ❤️ node-postgres so much, we created shorthands and helpers for it:
const sql = require('@sequencework/sql/pg') // ⚠️ we import @sequencework/sql/pg // main export stays the same const query = sql`select * from movies where id = ${id}` // sql.raw is also there const booksTable = 'books' const booksQuery = sql`select * from ${sql.raw(booksTable)}` // default pg result object: https://node-postgres.com/api/result const { rows, rowCount } = await sql.query(db)`select * from movies` // helpers const movies = await sql.many(db)`select * from movies` const movie = await sql.one(db)`select * from movies where id = ${id}` const nbMovie = await sql.count( db )`update from movies set name = ${name} where id = ${id}`
You can then rewrite the previous listMoviesByYear
function in a much more concise way 😎
const sql = require('@sequencework/sql/pg') // ⚠️ we import @sequencework/sql/pg const listMoviesByYear = async (db, yearRange) => sql.many(db)` select * from movies where year >= ${yearRange[0]} and year <= ${yearRange[1]} `
sql
comes with its TypeScript declaration file. You can directly use it within your TypeScript projects:
import sql = require('@sequencework/sql') const yearRange: ReadonlyArray<number> = [1983, 1992] const query = sql` select * from movies where year >= ${yearRange[0]} and year <= ${yearRange[1]} `
This package is inspired by the great sql-template-strings. Some interesting features that we were missing:
- nested
sql
tags - ignore
undefined
expressions insql
So we made this 🙂