Transforms a template literal in an object that can be read by node-postgres.
- 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)
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']// }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=dbFinally, 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)}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]}`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]}`This package is inspired by the great sql-template-strings. Some interesting features that we were missing:
- nested
sqltags - ignore
undefinedexpressions insql
So we made this 🙂
