Skip to content

Repository files navigation

sql``

javascript template literals to format sql

Transforms a template literal in an object that can be read by node-postgres.

npm versioninstall sizecirclecicoverage

Features

  • 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

Installation

npm install @sequencework/sql --save

(or with yarn, yarn add @sequencework/sql)

Usage

constsql=require('@sequencework/sql')constyearRange=[1983,1992]constquery=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:

constsql=require('@sequencework/sql')constfindBookByAuthor=author=>sql` select * from books${// if author is undefined, it is ignored in the queryauthor&&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']// }

⚠️ The expression will only be ignored if it returns undefined. If it is false, it will be added as a value.

constfilterThisYear=false// does not work as expectedsql` select * from books${filterThisYear&&sql`where year = 2018`}`// instead you should dosql` 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:

consttableName='books'constquery=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.jsconstsql=require('@sequencework/sql')constlistMoviesByYear=async(db,yearRange)=>{const{ rows }=awaitdb.query(sql` select * from movies where  year >= ${yearRange[0]} and year <= ${yearRange[1]} `)returnrows}module.exports={ listMoviesByYear }

Then, we create a singleton for the connection pool, like recommended by brianc, node-postgres's creator.

// db.jsconst{ Pool }=require('pg')// we create a singleton here for the connection poolconstdb=newPool()module.exports=db

Finally, we connect everything:

// main.jsconstdb=require('./db')const{ listMoviesByYear }=require('./movies')constmain=async()=>{constmovies=awaitlistMoviesByYear(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):

constmain=async()=>{// we get a clientconstclient=awaitdb.connect()try{awaitclient.query('BEGIN')constmovies=awaitlistMoviesByYear(client,[1983,1992])awaitclient.query('COMMIT')}catch(e){awaitclient.query('ROLLBACK')}finally{client.release()}console.log(movies)}

Shorthand for postgres

Since we ❤️ node-postgres so much, we created shorthands and helpers for it:

constsql=require('@sequencework/sql/pg')// ⚠️ we import @sequencework/sql/pg// main export stays the sameconstquery=sql`select * from movies where id = ${id}`// sql.raw is also thereconstbooksTable='books'constbooksQuery=sql`select * from ${sql.raw(booksTable)}`// default pg result object: https://node-postgres.com/api/resultconst{ rows, rowCount }=awaitsql.query(db)`select * from movies`// helpersconstmovies=awaitsql.many(db)`select * from movies`constmovie=awaitsql.one(db)`select * from movies where id = ${id}`constnbMovie=awaitsql.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 😎

constsql=require('@sequencework/sql/pg')// ⚠️ we import @sequencework/sql/pgconstlistMoviesByYear=async(db,yearRange)=>sql.many(db)` select * from movies where  year >= ${yearRange[0]} and year <= ${yearRange[1]}`

Usage with TypeScript

sql comes with its TypeScript declaration file. You can directly use it within your TypeScript projects:

importsql= require('@sequencework/sql')constyearRange: ReadonlyArray<number>=[1983,1992]constquery=sql` select * from movies where year >= ${yearRange[0]} and year <= ${yearRange[1]}`

More

This package is inspired by the great sql-template-strings. Some interesting features that we were missing:

  • nested sql tags
  • ignore undefined expressions in sql

So we made this 🙂

About

Javascript tag to format SQL template literals

Topics

Resources

Stars

31 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages